2023.10.25

发布时间 2023-10-26 02:37:25作者: shyiaw

T1

题面

image

解题

  1. 发现 \(k\le7\),故考虑用容斥定理。设 \(ans_i\) 表示不通过 \(i\) 个必经城市的方案数,则最终的输出答案为 \(ans_0-\sum\limits_{i=1}^{k}(-1)^{k-1}ans_i\)
  2. 考虑如何统计 \(ans_i\)。发现总状态数不大于 \(2^k\le2^7\),故枚举每个必经城市的通过与否,得到每一种状态最后的方案数,进而得到 \(ans_i\)
  3. 考虑如何获得每一种状态最后的方案书。设 \(f(i,u)\) 表示第 \(i\) 天在 \(u\) 的方案数,则:\(f(i,u)=\sum\limits_{v 与 u 直接相连}f(i-1,v),\)。暴力枚举时间复杂度超时,选择使用矩阵快速幂加速递推。得到一个状态最后的方案数的时间复杂度为 \(\mathcal O(n^3\log d)\)
  4. 时间复杂度为 \(\mathcal O(2^k\times n^3\log d)\)

代码

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxm=160;
const int maxn=25;
const int MOD=1e9+9;
struct edge
{
	int u,v;
} E[maxm];
vector<int> e[maxn];
int n,m,ki,d,mst[maxn],ans[maxn],Ans;
bool vis[maxm];
struct Matrix
{
	int lenr,lenc;
	int e[maxn][maxn];
	Matrix(){lenr=lenc=0;memset(e,0,sizeof(0));}
	Matrix operator *(Matrix B)
	{
		Matrix C;
		memset(C.e,0,sizeof(C.e));
        C.lenr=lenr,C.lenc=B.lenc;
		for(int k=1;k<=lenc;k++)
			for(int i=1;i<=lenr;i++)
			 for(int j=1;j<=B.lenc;j++)
			 	C.e[i][j]=((1ll*C.e[i][j]+1ll*e[i][k]*B.e[k][j]%MOD)%MOD+MOD)%MOD;
		return C;
	}
	int sum()
	{
		int ansi=0;
		for(int i=1;i<=lenr;i++)
			for(int j=1;j<=lenc;j++)
				ansi=((ansi+e[i][j])%MOD+MOD)%MOD;
		return ansi;
	}
	void print()
	{
		for(int i=1;i<=lenr;i++)
		{
			for(int j=1;j<=lenc;j++)
				cout<<e[i][j]<<" ";
			cout<<endl;
		}
	}
} I,A,A0;
void buildA()
{
	memset(A.e,0,sizeof(A.e));
	A.lenr=A.lenc=n;
	for(int u=1;u<=n;u++)
	{
		vector<int>::iterator it;
		for(it=e[u].begin();it!=e[u].end();it++)
			if(!vis[*it])
			{
				int ui=E[*it].u,vi=E[*it].v;
				if(ui!=u) swap(ui,vi);
				A.e[u][vi]=1;
			}
	}
}
void Init()
{
	I.lenr=I.lenc=n;
	memset(I.e,0,sizeof(I.e));
	for(int i=1;i<=n;i++)
		I.e[i][i]=1;
	A0.lenr=n,A0.lenc=1;
	for(int i=1;i<=n;i++)
		A0.e[i][1]=1;
}
Matrix ksm(Matrix A,int b)
{
	if(b==0) return I;
	if(b==1) return A;
	Matrix mid=ksm(A,b>>1);
	if(b%2==0) return mid*mid;
	else return mid*mid*A;
}
signed main()
{
	scanf("%lld%lld%lld%lld",&n,&m,&ki,&d);
	Init();
	//I.print(),A0.print();
	for(int i=1;i<=ki;i++) scanf("%lld",&mst[i]);
	for(int i=1;i<=m;i++)
	{
		int u,v; scanf("%lld%lld",&u,&v);
		E[i]=(edge){u,v};
		e[u].push_back(i),e[v].push_back(i);
	}
	for(int st=0;st<(1<<ki);st++)
	{
		memset(vis,false,sizeof(vis));
		int idx=0,curst=st;
		for(int i=1;i<=ki;i++)
		{
			if(curst&1) 
			{
				idx++;
				vector<int>::iterator it;
				for(it=e[mst[i]].begin();it!=e[mst[i]].end();it++)
					vis[*it]=true;
			}
			curst>>=1;
		}
		buildA();//cout<<st<<"here\n";//A.print();
		Matrix ANS=ksm(A,d-1)*A0;//ANS.print();
		ans[idx]=((ans[idx]+ANS.sum())%MOD+MOD)%MOD;
	}
	int sgn=1;
	for(int i=0;i<=ki;i++)
		Ans=((Ans+sgn*ans[i]%MOD)%MOD+MOD)%MOD,
		//cout<<ans[i]<<" "<<i<<endl;
		sgn*=-1;
	printf("%lld",Ans%MOD);
	return 0;
}
/*
4 4 2 3
1 2
1 2
2 3
3 1
2 4
*/

T2

题面

image

解题

  1. 发现每一列的状态只有白白白黑黑白三种,且白黑黑白可实现连续交替存在。故定义白白为白块,白黑与黑白为黑块。
  2. 假设目前 \(k\) 个黑块连续,则可先将黑块分为 \(i\) 个不相连的连续段,枚举用于划分黑块段的白块段的位置,有 \(C_{k-1}^{i-1}\) 中方案。接下来,将白块插入进序列中,其中应满足两个黑块段之间的白块数不能为 \(0\),两端黑块段的外则的白块数可以为 \(0\),故有 \(C_{n-k+1}^{i}\)种方案。在枚举每个黑块段的起始黑块的模式,故最终答案为 \(\sum\limits_{i=1}^{\min(k,n-k+1)}2^iC_{k-1}^{i-1}C_{n-k+1}^{i}\)