其他分享
首页 > 其他分享> > 杂题选做(Fate三部曲)

杂题选做(Fate三部曲)

作者:互联网

在一个珂朵莉泛滥的世界里找到三道有关\(fate\)的题多么不容易

Emiya家今天的饭

非常不错的一道容斥+\(dp\)题

考虑第三个限制,发现最多只有一列不合法,那么我们可以枚举不合法的一列是哪一列

那么接下来的任务就是求出总方案数-不合法的方案数

那么首先考虑如何计算不合法的方案数

令\(s_i\)表示每一行的\(\sum a_{i,j}\),\(col\)表示枚举到的不合法的那一列

设\(f_{i,j,k}\)表示考虑到了第\(i\)行,不合法的那一列选了\(j\)行,剩下的列总共选了\(k\)行的方案数

那么可以得到转移

\(f_{i,j,k}=f_{i-1,j,k}+a_{i,col}\times f_{i-1,j-1,k}+(s_i-a_{i,col})\times f_{i-1,j,k-1}\)

那么对于一种不合法的列\(col\),他的方案数就是\(\sum\limits_{j>k} f_{n,j,k}\)

接下来考虑如何计算总方案数

设\(g_{i,j}\)表示考虑到第\(i\)列选择了\(j\)行的方案数,那么转移更简单一些

\(g_{i,j}=g_{i-1,j}+s_i\times g_{i-1,j-1}\)

总方案数就是\(\sum\limits_{j=1}^{n}g_{n,j}\)

这样的话复杂度是\(O(n^3m)\)的,预计得分\(84\),考虑优化枚举次数

发现\(f_{i,j,k}\)中知道\(j,k\)的具体数值并没有什么意义,

那么考虑去掉一维变成\(f_{i,j}\)表示考虑到第\(i\)行,不合法的那一列选的行数其他列选的行数差值为\(j\),也就是原来的\(j-k\)

这样转移就变成了\(O(n^2m)\),预计得分\(100\)

code
#include<cstdio>
#include<cstring>
#include<iostream>
#define LL long long
const int NN=105,MM=2005;
const LL mod=998244353;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](LL x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
	};
}using namespace AE86;
int n,m,a[NN][MM];
LL ans,s[NN],f[2][NN<<1],g[2][NN],tot,r[NN][MM];
namespace WSN{
	inline short main(){
		// freopen("meal.in","r",stdin);
		// freopen("meal.out","w",stdout);
		n=read();m=read();
		for(int i=1;i<=n;++i){
			for(int j=1;j<=m;++j)
				a[i][j]=read(),s[i]=(s[i]+a[i][j])%mod;
			for(int j=1;j<=m;++j) r[i][j]=(s[i]-a[i][j]+mod)%mod;
		}
		for(int col=1;col<=m;++col){
			memset(f,0,sizeof(f));f[0][n]=1;
			for(int i=1;i<=n;++i)
				for(int j=n-i;j<=n+i;++j)
					f[i&1][j]=(f[(i-1)&1][j]+(j>0?f[(i-1)&1][j-1]*a[i][col]%mod:0)+(j<n*2?f[(i-1)&1][j+1]*r[i][col]%mod:0))%mod;
			int tmp=0;
			for(int j=1;j<=n;++j) tmp=(tmp+f[n&1][n+j])%mod;
			ans=(ans+tmp)%mod;
		}
		g[0][0]=1;
		for(int i=1;i<=n;++i)
			for(int j=0;j<=n;++j)
				g[i&1][j]=(g[(i-1)&1][j]+(j>0?s[i]*g[(i-1)&1][j-1]%mod:0))%mod;
		for(int j=1;j<=n;++j) tot=(tot+g[n&1][j])%mod;
		write((tot-ans+mod)%mod);
		return 0;
	}
}
signed main(){return WSN::main();}

\(tips\):

容斥,动态规划

王之财宝(Gate of Babylon)

比较经典的容斥题目,考虑题意的转化

有一个不定方程\(x_1+x_2+...+x_n\leq m\),其中一些\(x_i\)有\(x_i\leq b_i\)的限制,求合法的不定方程的非负整数解有几个。

注意:以下说的解均为是非负整数解

先来考虑不定方程\(x+y+z=10\)的解有几个,用插板法可知其解有\(C_12^2\)个

那么可知有\(n\)个未知数的不定方程\(x_1+...+x_n=m\)解有\(C_{m+n-1}^{n-1}\)个

考虑\(x+y+z\leq 10\)的解,亦使用插板法得到有\(C_13^3\)个,因为可以选出一堆来空出空间

那么在没有限制的情况下\(x_1+...+x_n\leq m\)的解有\(C_{m+n}^{n}\)个

继续考虑有限制的情况下\(x+y+z\leq 10\)的解,假设\(x\leq 6\)

这个问题可以考虑容斥,用总的解减去超越限制的解就是有限制的解,考虑计算超越限制的解

我们可以整一个新的\(xx=x+7\),那么对于\(x\leq 6\),都有\(xx<0\),也就是说\(xx+7+y+z\leq 10\)的解个数为\(0\),

然后继续使用插板法可以得到超限制的解的个数是\(C_6^3\),那么最后\(x+y+z\leq 10\)且\(x\leq 6\)的解的个数就是\(C_13^3-C_6^3\)

题目中有多组限制,就考虑使用容斥,奇减偶加,\(2^T\)枚举所有可能状态,然后计算合法解的个数即可

code
#include<cstdio>
#include<cstring>
#include<iostream>
#define int long long
const int NN=1e5+5;
using namespace std;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
	};
}using namespace AE86;
int n,m,t,mod,b[16],ans;
namespace Math{
	int h[NN],v[NN];
	auto qmo=[](int a,int b,int ans=1){
		int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c;
		return ans;
	};
	auto inv=[](int x){return qmo(x,mod-2);};
	auto prework=[](){
		h[0]=h[1]=1; v[0]=v[1]=1;
		for(int i=2;i<mod;++i) h[i]=h[i-1]*i%mod;
		for(int i=2;i<mod;++i) v[i]=v[i-1]*inv(i)%mod;
	};
	auto C=[](int n,int m){return (n<m||n<0||m<0)?0:h[n]*v[n-m]%mod*v[m]%mod;};
	inline int lucas(int n,int m){return m?lucas(n/mod,m/mod)*C(n%mod,m%mod)%mod:1;}
}using namespace Math;
namespace WSN{
	inline short main(){
		n=read(); t=read(); m=read(); mod=read(); prework();
		for(int i=1;i<=t;i++) b[i]=read();
		for(int i=0;i<(1<<t);i++){
			int tmp=m,bs=(__builtin_popcount(i)&1)?-1:1;
			for(int j=1;j<=t;j++)if(i&(1<<j-1))tmp-=(b[j]+1);
			ans=(ans+bs*lucas(tmp+n,n)+mod)%mod;
		}
		write(ans);
		return 0;
	}
}
signed main(){return WSN::main();}

王者之剑(Excalibar)

直接二分图最大权独立集,跑网络流就行了,至于为什么我是看别人的详细证明才会的,所以想要知其所以然的右转就好了

code(三道题里最具特色的代码,因为题面主人公是呆毛,所以还是忍不住中二了一下)
#include<cstdio>
#include<cstring>
#include<iostream>
#define int long long
const int NN=5e5+5,inf=1e18;
using namespace std;
namespace Lancer{
	auto Excalibar=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto Enuma_Elish=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
	};
}using namespace Lancer;
int n,m,w[101][101],ans;
int dx[4]={0,0,-1,1},
	dy[4]={-1,1,0,0};
struct Caster{int to,val,next;}e[NN<<1];int head[NN],rp=1;
auto add=[](int x,int y,int z){
	e[++rp]=Caster{y,z,head[x]};head[x]=rp;
	e[++rp]=Caster{x,0,head[y]};head[y]=rp;
};
namespace Archer{
	int HD[NN],q[NN],h,t,dis[NN],S,T;
	auto bfs=[](){
		memset(dis,0x3f,sizeof(dis));
		memcpy(head,HD,sizeof(head));
		q[h=t=1]=S; dis[S]=0;
		while(h<=t){
			int x=q[h++];
			for(int i=head[x];i;i=e[i].next)if(e[i].val)
				if(dis[e[i].to]>dis[x]+1)
					dis[e[i].to]=dis[x]+1,q[++t]=e[i].to;
			if(x==T) return 1;
		} return 0;
	};
	inline int dfs(int x,int in){
		if(x==T) return in;
		int rest=in,go;
		for(int i=head[x];i;head[x]=i=e[i].next)if(e[i].val){
			int y=e[i].to,v=e[i].val;
			if(dis[y]==dis[x]+1){
				go=dfs(y,min(rest,v));
				if(go) e[i].val-=go,e[i^1].val+=go,rest-=go;
				else dis[y]=0;
			}if(!rest) break;
		} return in-rest;
	}
	auto dinic=[](int ans=0){
		memcpy(HD,head,sizeof(HD));
		while(bfs()) ans+=dfs(S,inf);
		return ans;
	};
	auto id=[](int x,int y){return (x-1)*m+y;};
}using namespace Archer;
namespace Saber{
	inline short main(){
		n=Excalibar(); m=Excalibar(); S=n*m+1; T=S+1;
		for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) w[i][j]=Excalibar(),ans+=w[i][j];
		for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
			if((i+j)&1){
				add(S,id(i,j),w[i][j]);
				for(int k=0;k<4;k++){
					int x=i+dx[k],y=j+dy[k];
					if(x>0&&x<=n&&y>0&&y<=m)
						add(id(i,j),id(x,y),inf);
				}
			}
			else add(id(i,j),T,w[i][j]);
		Enuma_Elish(ans-dinic());
		return 0;
	}
}
signed main(){return Saber::main();}

标签:ch,Fate,leq,三部曲,namespace,int,ans,include,杂题
来源: https://www.cnblogs.com/hzoi-wsn/p/15478531.html