FCS省选模拟赛 Day1

Description

 Solution



T1 shopping

目测是插板法乱搞一下

发现题解写的是容斥dp:

\[ans = \sum_i (-1)^ig[i] \]

\(g[i]\)表示的有\(i\)个商店必然达到上限的方案数

考虑转化,设\(f[i][j]\)表示前\(i\)个商店,必然超过限制的商店的(上限+1)的和是\(j\)

\[f[i][j]=f[i-1][j]-f[i-1][j-w[i]-1] \]

所以答案就可以这样计算:

\[ans=\sum_i f[n][i] C(k+n-1,n-1) \]

后面那一块就是插板法

/*容斥dp */
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int 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<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
const int MN=5e6+5,MM=301,mod=1e9+7;
int n,m,k,wi[MM],f[2][MM*MM+MN],fac[MN<<1],inv[MN<<1];
int C(int x,int y){if(y<=0)return 1;return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;}
int fpow(int x,int M){int r=1;for(;M;M>>=1,x=1ll*x*x%mod)if(M&1)r=1ll*r*x%mod;return r;}
int main()
{
	register int i,j,qz=0,ans=0;
	n=read(),m=read(),k=read();
	for(i=1;i<=m;++i) wi[i]=read(),qz+=wi[i];
	for(fac[0]=i=1;i<=n+k;++i)fac[i]=1ll*fac[i-1]*i%mod;
	for(inv[n+k]=fpow(fac[n+k],mod-2),i=n+k-1;~i;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod;
	f[1][wi[1]+1]=mod-1;f[1][0]=1;
	for(i=2;i<=m;++i)
	{
		for(j=0;j<=qz+m;++j)
		{
			int l=j-wi[i]-1;f[i&1][j]=f[(i&1)^1][j];
			if(l>=0) f[i&1][j]+=mod-f[(i&1)^1][l],f[i&1][j]%=mod;
		}
	}
	for(i=0;i<=k;++i)ans+=1ll*f[m&1][i]*C(k+n-i-1,n-1)%mod,ans%=mod;
	printf("%d\n",ans);
}



T2 highway

发现\(n\)很小,所以对于已经按照边权排号序的边集,求\(Kruskal\)生成森林的只要\(O(\alpha(n)n)\)

考虑线段树维护区间,每个节点保存用到的边(有序的),合并的时候做一次\(Kruskal\)即可

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int 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<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
#define reg register
const int MN=105,MM=1e5+5;
int n,m,q;
struct edge{int u,v,w;}e[MM];
struct Node
{
	vector<int> ed;
	int val;
	Node(){ed.clear();val=0;}
}T[MM<<2];
int fa[MN];
int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);}
bool union_(int x,int y){x=getf(x);y=getf(y);if(x==y)return false;fa[x]=y;return true;}
Node merge(Node x,Node y)
{
	reg int si=x.ed.size(),sj=y.ed.size(),i,j;
	for(i=1;i<=n;++i) fa[i]=i;
	#define J y.ed[j]
	#define I x.ed[i]
	Node r;
	for(i=0,j=0;i<si||j<sj;)
	{
		if(i==si||(j!=sj&&e[J].w<e[I].w))
		{
			if(union_(e[J].u,e[J].v)) r.ed.push_back(J),r.val+=e[J].w;
			++j;continue;
		}
		if(union_(e[I].u,e[I].v)) r.ed.push_back(I),r.val+=e[I].w;
		++i;
	}
	return r;
}
void build(int x,int l,int r)
{
	if(l==r){T[x].ed.push_back(l);T[x].val=e[l].w;return;}
	int mid=(l+r)>>1;
	build(x<<1,l,mid);build(x<<1|1,mid+1,r);
	T[x]=merge(T[x<<1],T[x<<1|1]);
}
Node query(int x,int l,int r,int a,int b)
{
	if(l==a&&r==b) return T[x];
	int mid=(l+r)>>1;
	if(b<=mid) return query(x<<1,l,mid,a,b);
	if(a>mid) return query(x<<1|1,mid+1,r,a,b);
	else return merge(query(x<<1,l,mid,a,mid),query(x<<1|1,mid+1,r,mid+1,b));
}
int main()
{
	n=read();m=read();q=read();
	reg int i,l,r;
	for(i=1;i<=m;++i) e[i].u=read(),e[i].v=read(),e[i].w=read();
	build(1,1,m);
	while(q--)
	{
		l=read(),r=read();
		printf("%d\n",query(1,1,m,l,r).val);
	}
	return 0;
}



T3 sailing

第一步,我们考虑舰队可以”整体地“位于哪几个位置

把环境和舰队的图都转化成一个\(01\)序列,可以位于一个位置当且仅当在某个区间中没有两个数同为\(1\)

考虑把其中一个序列倒序处理,就可以用\(NTT\)算出上面的答案

第二步,可以位于某个位置并不代表一定可以移动的到

从一个一定满足的位置(比如说舰队的原位置)开始\(bfs\),显然所有合法的位置都是相互联通的

第三步,求可以到达的格子数

一个格子可以到达,说明将舰队的\(01\)序列放在某个合法的位置上,这个位置是\(1\)

显然也可以用\(NTT\)来解决

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int 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<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
#define reg register
const int MN=705,P=998244353,g=3,invg=332748118;
int n,m;
int a[MN*MN*4],b[MN*MN*4],c[MN*MN*4],N,di,invN,pos[MN*MN*4];
char mp[MN][MN];
bool rea[MN][MN];
int fpow(int x,int m){int r=1;for(;m;m>>=1,x=1ll*x*x%P)if(m&1)r=1ll*r*x%P;return r;}
void NTT(int *a,int type)
{
	reg int wi,w,i,j,p,k;
	for(i=0;i<N;++i)if(i<pos[i])std::swap(a[i],a[pos[i]]);
	for(i=1;i<N;i<<=1)
	{
		wi=fpow(type>0?g:invg,(P-1)/(i<<1));
		for(j=i<<1,p=0;p<N;p+=j)
		for(w=1,k=0;k<i;++k,w=1ll*w*wi%P)
		{
			int X=a[p+k],Y=1ll*a[i+p+k]*w%P;
			a[p+k]=(X+Y)%P;a[i+p+k]=(X-Y+P)%P;
		}
	}
	if(type<0)for(i=0;i<N;++i)a[i]=1ll*a[i]*invN%P;
}
std::queue<std::pair<int,int> > q;
const int dx[4]={0,-1,0,1},dy[4]={1,0,-1,0};
void bfs(int x,int y)
{
	q.push(make_pair(x,y));rea[x][y]=false;
	while(!q.empty())
	{
		int X=q.front().first,Y=q.front().second;q.pop();
		a[(X-1)*m+Y]=1;
		for(reg int i=0;i<4;++i)if(rea[X+dx[i]][Y+dy[i]])
			rea[X+dx[i]][Y+dy[i]]=false,q.push(make_pair(X+dx[i],Y+dy[i]));
	}
}
int main()
{
	int i,j,x1=P,x2=-P,y1=P,y2=-P,ans=0;
	n=read();m=read();
	for(i=1;i<=n;++i) scanf("%s",mp[i]+1);
	for(i=1;i<=n;++i)for(j=1;j<=m;++j)
	{
		if(mp[i][j]=='#') a[(i-1)*m+j]=1;
		if(mp[i][j]=='o') x1=min(x1,i),x2=max(x2,i),y1=min(y1,j),y2=max(y2,j);
	}
	int T=(x2-x1)*m+y2-y1+1;
	for(i=1;i<=n;++i)for(j=1;j<=m;++j)
		if(mp[i][j]=='o') b[(i-x1)*m+j-y1+1]=c[T-(i-x1)*m-j+y1]=1;
	for(N=1,di=0;N<=m*n*2;N<<=1,++di);invN=fpow(N,P-2);
	for(i=0;i<N;++i) pos[i]=(pos[i>>1]>>1)|((i&1)<<(di-1));
	NTT(a,1);NTT(b,1);NTT(c,1);
	for(i=0;i<N;++i) a[i]=1ll*a[i]*c[i]%P;
	NTT(a,-1);
	for(i=1;i+(x2-x1)<=n;++i)for(j=1;j+(y2-y1)<=m;++j)if(a[T+(i-1)*m+j]==0)rea[i][j]=true;
	memset(a,0,sizeof a);bfs(x1,y1);NTT(a,1);
	for(i=0;i<N;++i) a[i]=1ll*a[i]*b[i]%P;
	NTT(a,-1);
	for(i=2;i<=n*m+1;++i) if(a[i]) ++ans;
	return 0*printf("%d\n",ans);
}




Blog来自PaperCloud,未经允许,请勿转载,TKS!

posted @ 2019-03-23 15:41  PaperCloud  阅读(361)  评论(0编辑  收藏  举报