zoukankan      html  css  js  c++  java
  • 牛客IOI周赛21-提高组

    比赛链接:https://ac.nowcoder.com/acm/contest/9798#question

    题号 标题 已通过代码 通过率 我的状态
    A 序列问题 点击查看 34/406 通过
    B 俄罗斯方块 点击查看 19/406 通过
    C 旅行没有商问题 点击查看 20/96 未通过

    A

    推式子题,主要是细节特别多。讨论 (q) 的范围,分 5 类。

    只需 (B_{min}-A_{max} geq q)

    (i=A_{max},j=B_{min}) , 考虑枚举 (i,j),则 (A,B) 集合中的其他数分别在 ([1,i-1],[j+1,n]) 中任取。

    易得:

    [ans=sum _{i=1}^{n} 2^{i-1} imes sum_{j=max{i+q,1}}^{n} 2^{n-j} ]

    由于此式不能优美地化简,对 (q) 分类。

    • $q geq n $ 时, (ans=0).

    • (q in [0,n-1]) 时,

      [ans=sum _{i=1}^{n-q} 2^{i-1} imes sum_{j=i+q}^{n} 2^{n-j} ]

      [= sum _{i=1}^{n-q} 2^{i-1} imes (2^{n-i-q+1}-1) ]

      [= (n-q-1) imes 2^{n-q} +1 ]

    • (qin [-n+1,-1]) 时,有两类,别漏了第二类。

      [ans=sum _{i=|q|+1}^{n} 2^{i-1} imes sum_{j=i+q}^{n} 2^{n-j}+sum _{i=1}^{|q|} 2^{i-1} imes sum_{j=1}^{n} 2^{n-j} ]

      [= sum _{i=|q|+1}^{n-q} 2^{i-1} imes (2^{n-i-q+1}-1)+(2^{|q|}-1) imes (2^n-1) ]

      [=(n-|q|) imes 2^{n+|q|}-2^n+2^{|q|}+(2^{|q|}-1) imes (2^n-1) ]

    • (q leq -n) 时,随便选 (ans=(2^n-1)^2)

    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const LL MOD=998244353;
    
    LL fpow(LL x,LL k,LL MOD)
    {
    	LL res=1; x%=MOD;
    	while(k) {
    		if(k&1) res=res*x%MOD;
    		x=x*x%MOD; k>>=1;
    	}
    	return res;
    }
    
    int T;
    LL n,q;
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    
    	scanf("%d",&T);
    	while(T--) {
    		scanf("%lld%lld",&n,&q);
    		if(q>=n) printf("0
    "); 
    		else if(q>=0) printf("%lld
    ",((n-q-1)%MOD*fpow(2,n-q,MOD)%MOD+1+MOD)%MOD);
    		else if(n+q<=0) printf("%lld
    ",(fpow(2,n,MOD)-1)*(fpow(2,n,MOD)-1)%MOD);
    		else if(q<0) printf("%lld
    ",
    			(((n+q)%MOD*fpow(2,n-q,MOD)%MOD-fpow(2,n,MOD)+fpow(2,-q,MOD))%MOD+
    				(fpow(2,-q,MOD)-1)*(fpow(2,n,MOD)-1)%MOD+MOD)%MOD);
    	}
    	return 0;
    }
    

    B

    B 是 大模拟,类似于 玛雅游戏。

    type=1 有 2 类,type=2 有 1 类,type=3 有4类。

    我不太想写,于是写一类交一次。

    最后,我只考虑了 6 类就 A 了。

    数据太水

    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const int N=11,INF=0x3f3f3f3f;
    
    struct State
    {
    	int a[N][N];
    	int tot;
    	State() { memset(a,0,sizeof a); tot=0; }
    	int get_height(int x)
    	{
    		for(int j=7;j>=1;j--) 
    			if(a[j][x]) return j;
    		return 0;
    	}
    	void suit()
    	{
    		int i,j,flag,k;
    		for(i=7;i>=1;i--) {
    			flag=0;
    			for(j=1;j<=6;j++) 
    				if(a[i][j]) flag++;
    			
    			if(flag==6) {
    				for(k=i;k<=7;k++) {
    					for(j=1;j<=6;j++) {
    						a[k][j]=a[k+1][j];
    					}
    				}
    				tot++;
    				suit();
    				return;
    			}
    		}
    	}
    	
    	void print() const
    	{
    		puts("===========================");
    		int i,j;
    		for(i=7;i>=1;i--,printf("
    ")) 
    			for(j=1;j<=6;j++)
    				printf("%d ",a[i][j]);
    		puts("===========================");
    	}
    };
    
    int n;
    int type[N];
    int ans;
    
    void dfs(int step,State s)
    {
    //	s.print();
    	if(step==n+1) {
    		ans=max(ans,s.tot);
    		return;
    	}
    	
    	int i,j,h;
    	State v;
    	
    	if(type[step]==1) {
    		// 横着放。 
    		for(i=0;i<3;i++) {
    			v=s;
    			h=0;
    			for(j=1;j<=4;j++) 
    				h=max(h,v.get_height(i+j));
    			
    			if(h+1<=7) {
    				for(j=1;j<=4;j++) 
    					v.a[h+1][i+j]=1;
    				v.suit();
    				dfs(step+1,v);
    			}
    		}
    		// 竖着放。 
    		for(i=1;i<=6;i++) {
    			v=s;
    			h=v.get_height(i);
    			
    			if(h+4<=7) {
    				for(j=1;j<=4;j++) v.a[h+j][i]=1;
    //				v.print();
    				v.suit();
    //				v.print();
    				dfs(step+1,v);
    			}
    		}
    	}
    	
    	else if(type[step]==2) {
    		for(i=0;i<5;i++) {
    			v=s;
    			h=0;
    			for(j=1;j<=2;j++) {
    				h=max(h,v.get_height(i+j));
    			}
    			if(h+2<=7) {
    //				v.print();
    				for(j=1;j<=2;j++)
    					v.a[h+1][i+j]=v.a[h+2][i+j]=1;
    //				v.print();
    				v.suit();
    //				v.print();
    				dfs(step+1,v);
    			} 
    		}
    	}
    	
    	else if(type[step]==3) {
    		// A.
    		for(i=0;i<4;i++) {
    			v=s,h=0;
    			
    			for(j=1;j<=3;j++)
    				h=max(h,v.get_height(i+j));
    			
    			if(h+2<=7) {
    				for(j=1;j<=3;j++)
    					v.a[h+1][i+j]=1;
    				v.a[h+2][i+3]=1;
    				
    				v.suit();
    				dfs(step+1,v);
    			}
    		}
    		// B.
    		for(i=0;i<5;i++) {
    			v=s,h=0;
    			for(j=1;j<=2;j++) {
    				h=max(h,v.get_height(i+j));
    			}
    			
    			if(h+3<=7) {
    				v.a[h+1][i+1]=1;
    				v.a[h+2][i+1]=1;
    				v.a[h+3][i+1]=1;
    				v.a[h+1][i+2]=1;
    				
    				v.suit();
    				dfs(step+1,v);
    			}
    		}
    		
    		// C.
    		// ***
    		// *
    		for(i=0;i<4;i++) {
    			v=s; h=0;
    			h=max(h,v.get_height(i+1)+1);
    			h=max(h,v.get_height(i+2));
    			h=max(h,v.get_height(i+3));
    			
    			if(h+1<=7) {
    				v.a[h][i+1]=1;
    				v.a[h+1][i+1]=1;
    				v.a[h+1][i+2]=1;
    				v.a[h+1][i+3]=1;
    				
    				v.suit();
    				dfs(step+1,v);
    			}
    		}
    	}
    
    }
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    	
    	int i;
    	scanf("%d",&n);
    	for(i=1;i<=n;i++) 
    		scanf("%d",&type[i]);
    	
    	State s;
    	dfs(1,s);
    	cout<<ans<<endl;
    	return 0;
    }
    

    话说输出 (n+1)/2 能获得 70pts.

    其实熟悉了此类dfs,写起来也不难。

    C

    链接:https://ac.nowcoder.com/acm/contest/9798/C
    来源:牛客网

    给出n个点,m条边的无向图。满足无重边、自环,不保证连通。某人在图上依次访问d个节点(即所经过的所有节点构成的序列长度为d)。n个点中有k个点必须至少经过一次。起点、终点任选。求满足条件的方案数对109+7取模的值

    想了一个状压,然后用矩乘优化,80pts.

    #include<queue>
    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const int N=20+1;
    const LL MOD=1e9+7;
    
    int n,m,day,city;
    
    LL f[N][64],g[N][64];
    // f[i][j][k] 表示 经过了 i 个城市,现在在 j , 经过想要城市的点集为 k 的方案数。 
    int a[N];
    int w[N][N];
    
    void solve()
    {
    	int i,j,k,u,v;
    	for(i=0;i<n;i++) {
    		if(~a[i]) f[i][1<<a[i]]=1;
    		else f[i][0]=1;
    	}
    	for(i=2;i<=day;i++) {
    //		for(k=0;k<(1<<city);k++)
    //			for(j=0;j<n;j++)
    //				printf("f[%d][%d] = %lld
    ",j,k,f[j][k]);
    		memcpy(g,f,sizeof g);
    		memset(f,0,sizeof f);
    		for(k=0;k<(1<<city);k++) {
    			for(j=0;j<n;j++) {
    				if(~a[j] && !((k>>a[j])&1)) continue;
    				
    				
    				if(~a[j]) {
    					u=k-(1<<a[j]);
    					for(v=0;v<n;v++) {
    						if(~a[v] && !((u>>a[v])&1)) continue;
    						if(!w[j][v]) continue;
    					
    						f[j][k]=(f[j][k]+g[v][u])%MOD;
    					}
    				} 
    			
    				u=k;		
    				for(v=0;v<n;v++) {
    					if(~a[v] && !((u>>a[v])&1)) continue;
    					if(!w[j][v]) continue;
    				
    					f[j][k]=(f[j][k]+g[v][u])%MOD;
    				}
    			}
    		}
    		
    
    	}
    	
    	LL ans=0;
    	for(i=0;i<n;i++)
    		ans=(ans+f[i][(1<<city)-1])%MOD;
    	printf("%lld
    ",ans%MOD);	
    }
    
    int get(int x,int y) { return x*(1<<city)+y+1; }
    
    struct Matrix 
    {
    	LL a[2000][2000];
    	int n,m;
    	Matrix(int n=0,int m=0) : n(n),m(m) {
    		memset(a,0,sizeof a);
    	}
    };
    
    Matrix mul(const Matrix &x,const Matrix &y)
    {
    	Matrix z;
    	z.n=x.n,z.m=y.m;
    	int i,j,k;
    	for(i=1;i<=z.n;i++) {
    		for(j=1;j<=z.m;j++) {
    			for(k=1;k<=y.n;k++) 
    				z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%MOD)%MOD;
    		}
    	}
    	return z;
    }
    
    Matrix fpow(Matrix x,LL k,Matrix res)
    {
    	while(k) {
    		if(k&1) res=mul(x,res);
    		x=mul(x,x); k>>=1;
    	}
    	return res;
    }
    
    void print(Matrix x)
    {
    	for(int i=1;i<=x.n;i++,printf("
    "))
    		for(int j=1;j<=x.m;j++) 
    			printf("%lld ",x.a[i][j]);
    	printf("
    ");
    }
    
    void Work()
    {
    	int i,j,k,u,v;
    	const int S=(1<<city);
    	const int Row=get(n-1,S);
    	Matrix A(Row,1);
    	Matrix C(Row,Row);
    	
    	for(i=0;i<n;i++) {
    		if(~a[i]) A.a[get(i,1<<a[i])][1]=1;
    		else A.a[get(i,0)][1]=1;
    	}
    //	print(A);
    	for(k=0;k<(1<<city);k++) {
    		for(j=0;j<n;j++) {
    			if(~a[j] && !((k>>a[j])&1)) continue;
    			
    			if(~a[j]) {
    				u=k-(1<<a[j]);
    				for(v=0;v<n;v++) {
    					if(~a[v] && !((u>>a[v])&1)) continue;
    					if(!w[j][v]) continue;
    
    					C.a[get(j,k)][get(v,u)]++;
    				}
    			} 
    		
    			u=k;		
    			for(v=0;v<n;v++) {
    				if(~a[v] && !((u>>a[v])&1)) continue;
    				if(!w[j][v]) continue;
    			
    				C.a[get(j,k)][get(v,u)]++;
    			}
    //			print(C);
    		}
    	}
    	A=fpow(C,day-1,A);
    //	print(C);
    //	print(A);
    	LL ans=0;
    	for(i=0;i<n;i++)
    		ans=(ans+A.a[get(i,(1<<city)-1)][1])%MOD;
    	printf("%lld
    ",ans%MOD);	
    }
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i;
    	int x,y;
    	
    	cin>>n>>m>>day>>city;
    	memset(a,-1,sizeof a);
    	for(i=0;i<city;i++) {
    		cin>>x;
    		x--;
    		a[x]=i;
    	}
    	for(i=1;i<=m;i++) {
    		cin>>x>>y;
    		x--,y--;
    		w[x][y]=w[y][x]=1;
    	}
    	
    //	if(day<=1000 || n<=5) solve();
    	Work();
    	return 0;
    }
    

    正解咕咕咕中。

    Update 2021.2.6:

    考虑容斥。

    对不选的点容斥即可。

    #include<queue>
    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const int N=20+1;
    const LL MOD=1e9+7;
    
    int n,m,day,city;
    int w[N][N],a[N];
    
    struct Matrix 
    {
    	LL a[N][N];
    	int n,m;
    	Matrix(int n=0,int m=0) : n(n),m(m) {
    		memset(a,0,sizeof a);
    	}
    };
    
    Matrix mul(const Matrix &x,const Matrix &y)
    {
    	Matrix z;
    	z.n=x.n,z.m=y.m;
    	int i,j,k;
    	for(i=1;i<=z.n;i++) {
    		for(j=1;j<=z.m;j++) {
    			for(k=1;k<=y.n;k++) 
    				z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%MOD)%MOD;
    		}
    	}
    	return z;
    }
    
    Matrix fpow(Matrix x,LL k,Matrix res)
    {
    	while(k) {
    		if(k&1) res=mul(x,res);
    		x=mul(x,x); k>>=1;
    	}
    	return res;
    }
    
    void print(Matrix x)
    {
    	for(int i=1;i<=x.n;i++,printf("
    "))
    		for(int j=1;j<=x.m;j++) 
    			printf("%lld ",x.a[i][j]);
    	printf("
    ");
    }
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i,j,k;
    	int x,y;
    	
    	cin>>n>>m>>day>>city;
    	Matrix C(n,n),B(n,n),A(n,1),D(n,1);
    	for(i=0;i<city;i++) 
    		cin>>a[i];
    	for(i=1;i<=m;i++) {
    		cin>>x>>y;
    		C.a[x][y]=C.a[y][x]=1;
    	}
    
    	for(i=1;i<=n;i++) D.a[i][1]=1;
    	LL ans=0;
    	for(i=0;i<(1<<city);i++) {
    		B=C,A=D;
    		int sign=1;
    		for(j=0;j<city;j++) { // 容斥,不经过哪些点。 
    			if((i>>j)&1) {
    				sign=-sign;
    				for(k=1;k<=n;k++) 
    					B.a[a[j]][k]=B.a[k][a[j]]=0;
    				A.a[a[j]][1]=0;
    			}
    		} 
    		A=fpow(B,day-1,A);
    		for(j=1;j<=n;j++) 
    			ans=(ans+A.a[j][1]*sign)%MOD;
    	}
    	
    	ans=(ans+MOD)%MOD;
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    遇到不经过比经过好做的,一定要想到容斥。

    时间复杂度:(O(2^kn^3logd))

    感谢 @DoMoRanSky 巨巨的指点。

    比赛链接:https://ac.nowcoder.com/acm/contest/9798#question

    题号 标题 已通过代码 通过率 我的状态
    A 序列问题 点击查看 34/406 通过
    B 俄罗斯方块 点击查看 19/406 通过
    C 旅行没有商问题 点击查看 20/96 未通过

    A

    推式子题,主要是细节特别多。讨论 (q) 的范围,分 5 类。

    只需 (B_{min}-A_{max} geq q)

    (i=A_{max},j=B_{min}) , 考虑枚举 (i,j),则 (A,B) 集合中的其他数分别在 ([1,i-1],[j+1,n]) 中任取。

    易得:

    [ans=sum _{i=1}^{n} 2^{i-1} imes sum_{j=max{i+q,1}}^{n} 2^{n-j} ]

    由于此式不能优美地化简,对 (q) 分类。

    • $q geq n $ 时, (ans=0).

    • (q in [0,n-1]) 时,

      [ans=sum _{i=1}^{n-q} 2^{i-1} imes sum_{j=i+q}^{n} 2^{n-j} ]

      [= sum _{i=1}^{n-q} 2^{i-1} imes (2^{n-i-q+1}-1) ]

      [= (n-q-1) imes 2^{n-q} +1 ]

    • (qin [-n+1,-1]) 时,有两类,别漏了第二类。

      [ans=sum _{i=|q|+1}^{n} 2^{i-1} imes sum_{j=i+q}^{n} 2^{n-j}+sum _{i=1}^{|q|} 2^{i-1} imes sum_{j=1}^{n} 2^{n-j} ]

      [= sum _{i=|q|+1}^{n-q} 2^{i-1} imes (2^{n-i-q+1}-1)+(2^{|q|}-1) imes (2^n-1) ]

      [=(n-|q|) imes 2^{n+|q|}-2^n+2^{|q|}+(2^{|q|}-1) imes (2^n-1) ]

    • (q leq -n) 时,随便选 (ans=(2^n-1)^2)

    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const LL MOD=998244353;
    
    LL fpow(LL x,LL k,LL MOD)
    {
    	LL res=1; x%=MOD;
    	while(k) {
    		if(k&1) res=res*x%MOD;
    		x=x*x%MOD; k>>=1;
    	}
    	return res;
    }
    
    int T;
    LL n,q;
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    
    	scanf("%d",&T);
    	while(T--) {
    		scanf("%lld%lld",&n,&q);
    		if(q>=n) printf("0
    "); 
    		else if(q>=0) printf("%lld
    ",((n-q-1)%MOD*fpow(2,n-q,MOD)%MOD+1+MOD)%MOD);
    		else if(n+q<=0) printf("%lld
    ",(fpow(2,n,MOD)-1)*(fpow(2,n,MOD)-1)%MOD);
    		else if(q<0) printf("%lld
    ",
    			(((n+q)%MOD*fpow(2,n-q,MOD)%MOD-fpow(2,n,MOD)+fpow(2,-q,MOD))%MOD+
    				(fpow(2,-q,MOD)-1)*(fpow(2,n,MOD)-1)%MOD+MOD)%MOD);
    	}
    	return 0;
    }
    

    B

    B 是 大模拟,类似于 玛雅游戏。

    type=1 有 2 类,type=2 有 1 类,type=3 有4类。

    我不太想写,于是写一类交一次。

    最后,我只考虑了 6 类就 A 了。

    数据太水

    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const int N=11,INF=0x3f3f3f3f;
    
    struct State
    {
    	int a[N][N];
    	int tot;
    	State() { memset(a,0,sizeof a); tot=0; }
    	int get_height(int x)
    	{
    		for(int j=7;j>=1;j--) 
    			if(a[j][x]) return j;
    		return 0;
    	}
    	void suit()
    	{
    		int i,j,flag,k;
    		for(i=7;i>=1;i--) {
    			flag=0;
    			for(j=1;j<=6;j++) 
    				if(a[i][j]) flag++;
    			
    			if(flag==6) {
    				for(k=i;k<=7;k++) {
    					for(j=1;j<=6;j++) {
    						a[k][j]=a[k+1][j];
    					}
    				}
    				tot++;
    				suit();
    				return;
    			}
    		}
    	}
    	
    	void print() const
    	{
    		puts("===========================");
    		int i,j;
    		for(i=7;i>=1;i--,printf("
    ")) 
    			for(j=1;j<=6;j++)
    				printf("%d ",a[i][j]);
    		puts("===========================");
    	}
    };
    
    int n;
    int type[N];
    int ans;
    
    void dfs(int step,State s)
    {
    //	s.print();
    	if(step==n+1) {
    		ans=max(ans,s.tot);
    		return;
    	}
    	
    	int i,j,h;
    	State v;
    	
    	if(type[step]==1) {
    		// 横着放。 
    		for(i=0;i<3;i++) {
    			v=s;
    			h=0;
    			for(j=1;j<=4;j++) 
    				h=max(h,v.get_height(i+j));
    			
    			if(h+1<=7) {
    				for(j=1;j<=4;j++) 
    					v.a[h+1][i+j]=1;
    				v.suit();
    				dfs(step+1,v);
    			}
    		}
    		// 竖着放。 
    		for(i=1;i<=6;i++) {
    			v=s;
    			h=v.get_height(i);
    			
    			if(h+4<=7) {
    				for(j=1;j<=4;j++) v.a[h+j][i]=1;
    //				v.print();
    				v.suit();
    //				v.print();
    				dfs(step+1,v);
    			}
    		}
    	}
    	
    	else if(type[step]==2) {
    		for(i=0;i<5;i++) {
    			v=s;
    			h=0;
    			for(j=1;j<=2;j++) {
    				h=max(h,v.get_height(i+j));
    			}
    			if(h+2<=7) {
    //				v.print();
    				for(j=1;j<=2;j++)
    					v.a[h+1][i+j]=v.a[h+2][i+j]=1;
    //				v.print();
    				v.suit();
    //				v.print();
    				dfs(step+1,v);
    			} 
    		}
    	}
    	
    	else if(type[step]==3) {
    		// A.
    		for(i=0;i<4;i++) {
    			v=s,h=0;
    			
    			for(j=1;j<=3;j++)
    				h=max(h,v.get_height(i+j));
    			
    			if(h+2<=7) {
    				for(j=1;j<=3;j++)
    					v.a[h+1][i+j]=1;
    				v.a[h+2][i+3]=1;
    				
    				v.suit();
    				dfs(step+1,v);
    			}
    		}
    		// B.
    		for(i=0;i<5;i++) {
    			v=s,h=0;
    			for(j=1;j<=2;j++) {
    				h=max(h,v.get_height(i+j));
    			}
    			
    			if(h+3<=7) {
    				v.a[h+1][i+1]=1;
    				v.a[h+2][i+1]=1;
    				v.a[h+3][i+1]=1;
    				v.a[h+1][i+2]=1;
    				
    				v.suit();
    				dfs(step+1,v);
    			}
    		}
    		
    		// C.
    		// ***
    		// *
    		for(i=0;i<4;i++) {
    			v=s; h=0;
    			h=max(h,v.get_height(i+1)+1);
    			h=max(h,v.get_height(i+2));
    			h=max(h,v.get_height(i+3));
    			
    			if(h+1<=7) {
    				v.a[h][i+1]=1;
    				v.a[h+1][i+1]=1;
    				v.a[h+1][i+2]=1;
    				v.a[h+1][i+3]=1;
    				
    				v.suit();
    				dfs(step+1,v);
    			}
    		}
    	}
    
    }
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    	
    	int i;
    	scanf("%d",&n);
    	for(i=1;i<=n;i++) 
    		scanf("%d",&type[i]);
    	
    	State s;
    	dfs(1,s);
    	cout<<ans<<endl;
    	return 0;
    }
    

    话说输出 (n+1)/2 能获得 70pts.

    其实熟悉了此类dfs,写起来也不难。

    C

    链接:https://ac.nowcoder.com/acm/contest/9798/C
    来源:牛客网

    给出n个点,m条边的无向图。满足无重边、自环,不保证连通。某人在图上依次访问d个节点(即所经过的所有节点构成的序列长度为d)。n个点中有k个点必须至少经过一次。起点、终点任选。求满足条件的方案数对109+7取模的值

    想了一个状压,然后用矩乘优化,80pts.

    #include<queue>
    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const int N=20+1;
    const LL MOD=1e9+7;
    
    int n,m,day,city;
    
    LL f[N][64],g[N][64];
    // f[i][j][k] 表示 经过了 i 个城市,现在在 j , 经过想要城市的点集为 k 的方案数。 
    int a[N];
    int w[N][N];
    
    void solve()
    {
    	int i,j,k,u,v;
    	for(i=0;i<n;i++) {
    		if(~a[i]) f[i][1<<a[i]]=1;
    		else f[i][0]=1;
    	}
    	for(i=2;i<=day;i++) {
    //		for(k=0;k<(1<<city);k++)
    //			for(j=0;j<n;j++)
    //				printf("f[%d][%d] = %lld
    ",j,k,f[j][k]);
    		memcpy(g,f,sizeof g);
    		memset(f,0,sizeof f);
    		for(k=0;k<(1<<city);k++) {
    			for(j=0;j<n;j++) {
    				if(~a[j] && !((k>>a[j])&1)) continue;
    				
    				
    				if(~a[j]) {
    					u=k-(1<<a[j]);
    					for(v=0;v<n;v++) {
    						if(~a[v] && !((u>>a[v])&1)) continue;
    						if(!w[j][v]) continue;
    					
    						f[j][k]=(f[j][k]+g[v][u])%MOD;
    					}
    				} 
    			
    				u=k;		
    				for(v=0;v<n;v++) {
    					if(~a[v] && !((u>>a[v])&1)) continue;
    					if(!w[j][v]) continue;
    				
    					f[j][k]=(f[j][k]+g[v][u])%MOD;
    				}
    			}
    		}
    		
    
    	}
    	
    	LL ans=0;
    	for(i=0;i<n;i++)
    		ans=(ans+f[i][(1<<city)-1])%MOD;
    	printf("%lld
    ",ans%MOD);	
    }
    
    int get(int x,int y) { return x*(1<<city)+y+1; }
    
    struct Matrix 
    {
    	LL a[2000][2000];
    	int n,m;
    	Matrix(int n=0,int m=0) : n(n),m(m) {
    		memset(a,0,sizeof a);
    	}
    };
    
    Matrix mul(const Matrix &x,const Matrix &y)
    {
    	Matrix z;
    	z.n=x.n,z.m=y.m;
    	int i,j,k;
    	for(i=1;i<=z.n;i++) {
    		for(j=1;j<=z.m;j++) {
    			for(k=1;k<=y.n;k++) 
    				z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%MOD)%MOD;
    		}
    	}
    	return z;
    }
    
    Matrix fpow(Matrix x,LL k,Matrix res)
    {
    	while(k) {
    		if(k&1) res=mul(x,res);
    		x=mul(x,x); k>>=1;
    	}
    	return res;
    }
    
    void print(Matrix x)
    {
    	for(int i=1;i<=x.n;i++,printf("
    "))
    		for(int j=1;j<=x.m;j++) 
    			printf("%lld ",x.a[i][j]);
    	printf("
    ");
    }
    
    void Work()
    {
    	int i,j,k,u,v;
    	const int S=(1<<city);
    	const int Row=get(n-1,S);
    	Matrix A(Row,1);
    	Matrix C(Row,Row);
    	
    	for(i=0;i<n;i++) {
    		if(~a[i]) A.a[get(i,1<<a[i])][1]=1;
    		else A.a[get(i,0)][1]=1;
    	}
    //	print(A);
    	for(k=0;k<(1<<city);k++) {
    		for(j=0;j<n;j++) {
    			if(~a[j] && !((k>>a[j])&1)) continue;
    			
    			if(~a[j]) {
    				u=k-(1<<a[j]);
    				for(v=0;v<n;v++) {
    					if(~a[v] && !((u>>a[v])&1)) continue;
    					if(!w[j][v]) continue;
    
    					C.a[get(j,k)][get(v,u)]++;
    				}
    			} 
    		
    			u=k;		
    			for(v=0;v<n;v++) {
    				if(~a[v] && !((u>>a[v])&1)) continue;
    				if(!w[j][v]) continue;
    			
    				C.a[get(j,k)][get(v,u)]++;
    			}
    //			print(C);
    		}
    	}
    	A=fpow(C,day-1,A);
    //	print(C);
    //	print(A);
    	LL ans=0;
    	for(i=0;i<n;i++)
    		ans=(ans+A.a[get(i,(1<<city)-1)][1])%MOD;
    	printf("%lld
    ",ans%MOD);	
    }
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i;
    	int x,y;
    	
    	cin>>n>>m>>day>>city;
    	memset(a,-1,sizeof a);
    	for(i=0;i<city;i++) {
    		cin>>x;
    		x--;
    		a[x]=i;
    	}
    	for(i=1;i<=m;i++) {
    		cin>>x>>y;
    		x--,y--;
    		w[x][y]=w[y][x]=1;
    	}
    	
    //	if(day<=1000 || n<=5) solve();
    	Work();
    	return 0;
    }
    

    正解咕咕咕中。

    Update 2021.2.6:

    考虑容斥。

    对不选的点容斥即可。

    #include<queue>
    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const int N=20+1;
    const LL MOD=1e9+7;
    
    int n,m,day,city;
    int w[N][N],a[N];
    
    struct Matrix 
    {
    	LL a[N][N];
    	int n,m;
    	Matrix(int n=0,int m=0) : n(n),m(m) {
    		memset(a,0,sizeof a);
    	}
    };
    
    Matrix mul(const Matrix &x,const Matrix &y)
    {
    	Matrix z;
    	z.n=x.n,z.m=y.m;
    	int i,j,k;
    	for(i=1;i<=z.n;i++) {
    		for(j=1;j<=z.m;j++) {
    			for(k=1;k<=y.n;k++) 
    				z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%MOD)%MOD;
    		}
    	}
    	return z;
    }
    
    Matrix fpow(Matrix x,LL k,Matrix res)
    {
    	while(k) {
    		if(k&1) res=mul(x,res);
    		x=mul(x,x); k>>=1;
    	}
    	return res;
    }
    
    void print(Matrix x)
    {
    	for(int i=1;i<=x.n;i++,printf("
    "))
    		for(int j=1;j<=x.m;j++) 
    			printf("%lld ",x.a[i][j]);
    	printf("
    ");
    }
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i,j,k;
    	int x,y;
    	
    	cin>>n>>m>>day>>city;
    	Matrix C(n,n),B(n,n),A(n,1),D(n,1);
    	for(i=0;i<city;i++) 
    		cin>>a[i];
    	for(i=1;i<=m;i++) {
    		cin>>x>>y;
    		C.a[x][y]=C.a[y][x]=1;
    	}
    
    	for(i=1;i<=n;i++) D.a[i][1]=1;
    	LL ans=0;
    	for(i=0;i<(1<<city);i++) {
    		B=C,A=D;
    		int sign=1;
    		for(j=0;j<city;j++) { // 容斥,不经过哪些点。 
    			if((i>>j)&1) {
    				sign=-sign;
    				for(k=1;k<=n;k++) 
    					B.a[a[j]][k]=B.a[k][a[j]]=0;
    				A.a[a[j]][1]=0;
    			}
    		} 
    		A=fpow(B,day-1,A);
    		for(j=1;j<=n;j++) 
    			ans=(ans+A.a[j][1]*sign)%MOD;
    	}
    	
    	ans=(ans+MOD)%MOD;
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    遇到不经过比经过好做的,一定要想到容斥。

    时间复杂度:(O(2^kn^3logd))

    感谢 @DoMoRanSky 巨巨的指点。

  • 相关阅读:
    git使用代理
    反汇编一个c程序
    Berkeley套接字
    ubuntu 升级
    ubuntu备份与还原
    struct socket 结构详解
    How sockets work
    wget中文乱码问题
    ubuntu配置开机启动服务
    《javascript设计模式》笔记之第五章:单体模式
  • 原文地址:https://www.cnblogs.com/cjl-world/p/14194689.html
Copyright © 2011-2022 走看看