2019.07.05【NOIP提高组】模拟 A 组(分治NTT模板、上下界网络流)

T1:这题很简单。

把所有边按频率排序,枚举选边的区间,然后用并查集维护加边就可以了。

 

T2:这一题是一个复杂的上下界网络流+动态加边。

首先我们建立源点和汇点sd、td,然后从sd向所有i连流量限制为(0,inf),费用为in[i]的边(简称(0,inf,in[i]),下同)。然后从i向td连(0,inf,out[i])。这两种边表示i可以随时向sd或td流流量以减少或增加自身的流量,但是要话费in或out的费用。

如果i的left为负,那么就i向td连(-left,-left,0),否则sd向i连(left,left,0)。而对于原图的一条边(x,y,a,b,down,up),我们就把它拆成a+b,3a+b,5a+b……那么我们就能表示出所有流量的情况了。

对于这一个图,跑一遍有上下界的费用流,跑出来的就是答案。

注意:实际上,一条x、y的最开始的边不是a+b,而是以down为流量的费用。而且我们要在网络流过程中不断调整x、y的费用,防止出错。因为我们是优先选a+b、再3a+b……以此类推。

至于有上下界的网络流,参考我写的“上下界网络流”这篇博客。

 

T3:首先我们要推出一个式子,设f[i]表示n=i时的答案,那么f[i]=2^C(i,2)-sum(f[j]*C(i-1,j-1)*2^C(i-j,2)),(1<=j<=i-1)。

这条式子的意思就是用总的方案数-不合法的方案数。总的方案数就是2^C(i,2)。至于不合法的方案数我们就枚举一个i的子点集,设大小为j,并且我们规定1号点在选出来的集合里面(防止重复计算),那么保证j大小的点集为联通块的方案数是f[j],从i个点中选j个点并且要包含1号点的方案数为C(i-1,j-1),最后剩下的i-j的点之间可以随便连边(反正这已经是一个不联通图了),那么我们就得出了上面这一条式子。

但计算这条式子的时间复杂度是n^2的。所以我们化简一下:

f[i]=2^C(i,2)-sum(f[j]*C(i-1,j-1)*2^C(i-j,2))

=2^C(i,2)-sum(f[j]*(i-1)!/(j-1)!/(i-j)!*2^C(i-j,2))

然后把1/(j-i)!与f[j]放在一起,1/(i-j)!与2^C(i-j,2)放在一起,(i-1)!提出来,那么原式就变成了

f[i]=2^(i,2)-(i-1)!*sum(f[j]/(j-1)!*2^C(i-j,2)/(i-j)!)。

设F[i]=f[i]/(i-1)!,G[i]=2^C(i,2)/i!,接着把上面式子同除(i-1)!,那么就得到了

F[i]=2^(i,2)/(i-1)!-sum(F[j]*G[i-j]),这显然就是一个卷积的形式。

我们的目的是求F(G是可以预处理出来的),但是我们又不能直接用NTT做,因为更新后面F要用到前面的F,所以我们考虑用分治NTT。

所谓的分治NTT就是每次计算l~mid的F对mid+1~r的贡献。

注意:1、当前l~r的区间只能用l~mid的F乘某些G去给mid+1~r的F贡献。不在mid+1~r区间里的F是不能被当前l~mid贡献的。这样做是为了防止重复计算。

2、“某些G”的具体范围是1~r-l+1,因为F[x]*G[y]是对F[x+y]贡献的,所以我们用l~mid的F去给mid+1~r做贡献,G的取值范围就是1~r-l+1。

下面贴一下代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#define ll long long
#define MAXN 520010

ll a[MAXN],b[MAXN],F[MAXN],G[MAXN],jc[MAXN],sum[MAXN],rev[MAXN],mod=1004535809,bit,s,n,g=3,ans;
ll sqr(ll x,ll y)
{
	ll w=y,j=x%mod,s=1;
	while(w>=1)
	{
		if(w%2==1)s=(s*j)%mod;
		j=(j*j)%mod;
		w/=2;
	}
	return s;
}
int getrev()
{
	ll i;
	for(i=0;i<s;i++)rev[i]=((rev[i>>1]>>1)|((i&1)<<(bit-1)));
}
int NTT(ll *a,ll inv)
{
	ll i,j,step,k,x,y,wn,wnk,t;
	for(i=0;i<s;i++)
		if(i<rev[i]){t=a[i];a[i]=a[rev[i]];a[rev[i]]=t;}
	for(step=1;step<s;step*=2)
	{
		if(inv==1)wn=sqr(g,(mod-1)/(step*2));
		else wn=sqr(sqr(g,(mod-1)/(step*2)),mod-2);
		for(j=0;j<s;j=j+step*2)
		{
			wnk=1;
			for(k=j;k<j+step;k++)
			{
				x=a[k];y=wnk*a[k+step]%mod;
				a[k]=(x+y)%mod;a[k+step]=(x-y+mod)%mod;
				wnk=(wnk*wn)%mod;
			}
		}
	}
}
int work()
{
	ll i,v;
	getrev();
	NTT(a,1);NTT(b,1);
	for(i=0;i<s;i++)a[i]=a[i]*b[i]%mod;
	NTT(a,-1);
	v=sqr(s,mod-2);
	for(i=0;i<s;i++)a[i]=a[i]*v%mod;
}
int dg(ll l,ll r)
{
	ll i,mid=(l+r)/2,left,right;
	if(l==r)
	{
		if(l!=1)F[l]=(sqr((ll)2,l*(l-1)/2)*sqr(jc[l-1],mod-2)%mod-sum[l]+mod)%mod;
		else F[l]=1;
		return 0;
	}
	dg(l,mid);
	left=1;right=r-l+1;
	s=1;bit=0;
	while(s<right-left+1)s*=2,bit++;
	for(i=0;i<s;i++)a[i]=0,b[i]=0;
	for(i=l;i<=mid;i++)a[i-l]=F[i];
	for(i=left;i<=right;i++)b[i-left]=G[i];
	for(i=mid-l+1;i<s;i++)a[i]=0;
	for(i=right-left+1;i<s;i++)b[i]=0;
	work();
	for(i=0;i<s;i++)
		if(i+l+left>=mid+1&&i+l+left<=r)sum[i+l+left]=(sum[i+l+left]+a[i])%mod;
	dg(mid+1,r);
}
int main()
{
ll i,j;
scanf("%lld",&n);
ans=n;n=1;
while(n<ans)n*=2;
jc[0]=1;for(i=1;i<=n*2;i++)jc[i]=(jc[i-1]*i)%mod;
G[1]=1;
for(i=2;i<=n*2;i++)G[i]=sqr((ll)2,i*(i-1)/2)*sqr(jc[i],mod-2)%mod;
dg(1,n);
printf("%lld",F[ans]*jc[ans-1]%mod);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值