• 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
    ",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
    ",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
    ",ans);
    }
    




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

  • 相关阅读:
    apk反编译
    mysql 安装解读
    安卓=--确认
    安卓--界面--改变image view
    安卓--返回时,不丢失转态
    安卓--跳转
    安卓--菜单
    安卓--Toast
    设置网页上收藏夹的图标
    分帧标签
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/FCS_noi2019_day1.html
Copyright © 2020-2023  润新知