联考20200618 T1 墨水大师

题目:

分析:
考虑树的情况,把根的颜色确定之后剩下的颜色只需要与父亲不同就好了,答案为(C(C-1)^{n-1})
考虑环的情况,答案需要减去终点和起点颜色相同的情况,强行合并起点终点答案为(C(C-1)^{n-1}),但是发现这样多减了终点和倒数第二个点相同的情况。。。
考虑容斥
假设一个环的长度为(n),答案为(sum_{i=1}^{n}(-1)^{i-1}C(C-1)^{n-i})
发现这是一个等比数列,当我们知道C之后就可以求出任意环大小的答案
运用一个有趣的性质
一个(n)个点的仙人掌最多由(sqrt{n})种大小的环组合起来
于是预处理仙人掌的环,然后对于每一个(C)暴力计算即可

复杂度(O(qsqrt{n}logMOD))(log)是求逆元
这玩意还被卡我无话可说,不优化了(2333

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>

#define maxn 400005
#define MOD 998244353

using namespace std;

inline int getint()
{
	int num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int n,m,q;
int fir[maxn],nxt[maxn],to[maxn],cnt;
int dfn[maxn],low[maxn],tim,stk[maxn],tp;
int b[maxn];
int Sz[maxn],Tim[maxn],cur;

inline void newnode(int u,int v)
{to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt;}

inline void tarjan(int u)
{
	dfn[u]=low[u]=++tim,stk[++tp]=u;
	for(int i=fir[u];i;i=nxt[i])
		if(!dfn[to[i]])
		{
			tarjan(to[i]),low[u]=min(low[u],low[to[i]]);
			if(low[to[i]]>=dfn[u])
			{
				int num=0,x;
				do{x=stk[tp--],num++;}while(x!=to[i]);
				b[num]++;
			}
		}
		else low[u]=min(low[u],dfn[to[i]]);
}

inline int ksm(int num,int k)
{
	int ret=1;
	for(;k;k>>=1,num=1ll*num*num%MOD)if(k&1)ret=1ll*ret*num%MOD;
	return ret;
}
inline int calc(int N,int C)
{
	int P=MOD-ksm(C-1,MOD-2),A=ksm(C-1,N);
	P=1ll*(1-ksm(P,N))*ksm(1-P,MOD-2)%MOD;
	return 1ll*A*P%MOD;
}

int main()
{
	n=getint(),m=getint(),q=getint();
	for(int i=1;i<=m;i++)
	{
		int u=getint(),v=getint();
		newnode(u,v),newnode(v,u);
	}
	tarjan(1);
	for(int i=1;i<=n;i++)if(b[i])Sz[++cur]=i,Tim[cur]=b[i];
	while(q--)
	{
		int C=getint();
		int tmp=1;
		for(int i=1;i<=cur;i++)
			tmp=1ll*tmp*ksm(calc(Sz[i],C),Tim[i])%MOD;
		printf("%lld
",1ll*C*tmp%MOD);
	}
}

原文地址:https://www.cnblogs.com/Darknesses/p/13160092.html