zoukankan      html  css  js  c++  java
  • Codeforces Round #446 Div. 1

      B:即使看到n<=22也应该猜到这只是为了写spj。将每个数替换为恰好比他大的数即可,最大值替换为最小值。这样原序列中不包含最小值的集合显然都满足条件,并且容易发现包含最小值的集合的变化量都是最大值-最小值+序列其他两个数的差,这显然是不会为0的。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define N 23
    char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
    int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
    int read()
    {
    	int x=0,f=1;char c=getchar();
    	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    	return x*f;
    }
    int n,a[N],id[N];
    bool cmp(const int&x,const int &y)
    {
    	return a[x]<a[y];
    }
    signed main()
    {
    	n=read();
    	for (int i=1;i<=n;i++) a[i]=read(),id[i]=i;
    	sort(id+1,id+n+1,cmp);
    	int x=a[id[n]];for (int i=n;i>=2;i--) a[id[i]]=a[id[i-1]];a[id[1]]=x;
    	for (int i=1;i<=n;i++) cout<<a[i]<<' ';
    	return 0;
    	//NOTICE LONG LONG!!!!!
    }
    

      C:容易想到随便跑一棵MST然后LCT维护MST,但常数过大。一个众所周知的结论是,所有MST中,所有权值相同的边对连通性的贡献是相同的。于是离线,按边权从小到大考虑,维护将小于当前边权的边加入MST后所得的并查集,对每个询问验证加入该种权值的边后是否会成环即可。当然需要使用带撤销的并查集。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define N 500010
    char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
    int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
    int read()
    {
    	int x=0,f=1;char c=getchar();
    	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    	return x*f;
    }
    int n,m,q,p[N],ans[N],cur[N],v[N];
    int fa[N],size[N],stk_id[N<<1],stk_fa[N<<1],stk_size[N<<1];
    vector<int> id[N],pos[N];
    struct data
    {
    	int x,y,z;
    	bool operator <(const data&a) const
    	{
    		return z<a.z;
    	}
    }edge[N],e[N];
    int find(int x){return fa[x]==x?x:find(fa[x]);}
    bool cmp(const int&x,const int&y)
    {
    	return edge[x].z<edge[y].z;
    }
    signed main()
    {
    #ifndef ONLINE_JUDGE
    	freopen("b.in","r",stdin);
    	freopen("b.out","w",stdout);
    #endif
    	n=read(),m=read();
    	for (int i=1;i<=m;i++) edge[i].x=read(),edge[i].y=read(),v[i]=edge[i].z=read();
    	sort(v+1,v+m+1);
    	int t=unique(v+1,v+m+1)-v-1;
    	q=read();
    	for (int i=1;i<=q;i++)
    	{
    		int m=read();ans[i]=1;
    		while (m--) id[i].push_back(read());
    		sort(id[i].begin(),id[i].end(),cmp);
    		for (int j=0;j<id[i].size();j++)
    		{
    			int x=lower_bound(v+1,v+t+1,edge[id[i][j]].z)-v;
    			if (!j||edge[id[i][j]].z!=edge[id[i][j-1]].z) pos[x].push_back(i);
    		}
    	}
    	for (int i=1;i<=m;i++) e[i]=edge[i];
    	sort(edge+1,edge+m+1);
    	for (int i=1;i<=n;i++) fa[i]=i,size[i]=1;
    	int u=0;
    	for (int i=1;i<=m;i++)
    	{
    		int t=i;u++;
    		while (t<m&&edge[t+1].z==edge[i].z) t++;
    		for (int j=0;j<pos[u].size();j++)
    		{
    			int x=pos[u][j];int top=0;
    			while (ans[x]&&cur[x]<id[x].size()&&e[id[x][cur[x]]].z==edge[i].z)
    			{
    				int p=find(e[id[x][cur[x]]].x),q=find(e[id[x][cur[x]]].y);
    				if (size[p]<size[q]) swap(p,q);
    				if (p==q) ans[x]=0;
    				else
    				{
    					top++;stk_id[top]=p;stk_size[top]=size[p];stk_fa[top]=p;
    					top++;stk_id[top]=q;stk_size[top]=size[q];stk_fa[top]=q;
    					fa[q]=p;size[p]+=size[q];
    				}
    				cur[x]++;
    			}
    			while (top) fa[stk_id[top]]=stk_fa[top],size[stk_id[top]]=stk_size[top],top--;
    		}
    		for (int j=i;j<=t;j++)
    		{
    			int p=find(edge[j].x),q=find(edge[j].y);
    			if (size[p]<size[q]) swap(p,q);
    			if (p!=q) fa[q]=p,size[p]+=size[q];
    		}
    		i=t;
    	}
    	for (int i=1;i<=q;i++) if (ans[i]) puts("YES");else puts("NO");
    	return 0;
    	//NOTICE LONG LONG!!!!!
    }
    

      E:自闭了把ai-=1看成了ai=1。那就先口胡一下这个东西的做法。

      考虑一个大小为j的子集在第i次被选中会提供多少贡献(即该子集的补集恰好全部被重置为1)。显然前i次选择的数应该均在其补集中且恰好覆盖整个补集,第i次之后的数任取。方案数即为S(i,n-j)·(n-j)!·nk-i

      这样我们先对每种大小的子集求其乘积之和。显然有f[i][j]表示前i位选了j个所有方案的乘积之和,转移显然。设f[n][i]=F[i]。

      则最后要求的答案就是ΣΣS(i,n-j)·(n-j)!·nk-i·F[j] (i=1~k j=0~n-1)。斯特林数套路地容斥一发,得ΣΣΣC(n-j,x)·xi·(-1)n-j-x·nk-i·F[j] (i=1~k j=0~n-1 x=1~n-j)。可以发现对于巨大的k,最后只剩下对x∈[1,n]每个x求出Σxi·nk-i。显然这是一个公比为x/n的等比数列,直接算就完了。然后O(n2)暴力算式子即可。并且容易发现这个东西是卷积形式,之前的dp也可以分治NTT。当然NTT什么就懒得写了。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define P 1000000007
    #define N 5010 
    char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
    int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
    int read()
    {
    	int x=0,f=1;char c=getchar();
    	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    	return x*f;
    }
    int n,k,a[N],f[N][N],F[N],G[N],C[N][N],ans;
    int ksm(int a,int k)
    {
    	int s=1;
    	for (;k;k>>=1,a=1ll*a*a%P) if (k&1) s=1ll*s*a%P;
    	return s;
    }
    int inv(int a){return ksm(a,P-2);}
    int calc(int first,int q,int n){if (q==1) return 1ll*first*n%P;return 1ll*first*(P+1-ksm(q,n))%P*inv(P+1-q)%P;}
    signed main()
    {
    #ifndef ONLINE_JUDGE
    	freopen("b.in","r",stdin);
    	freopen("b.out","w",stdout);
    #endif
    	n=read(),k=read();
    	for (int i=1;i<=n;i++) a[i]=read();
    	f[0][0]=C[0][0]=1;
    	for (int i=1;i<=n;i++)
    	{
    		f[i][0]=C[i][0]=1;
    		for (int j=1;j<=i;j++)
    		{
    			f[i][j]=(f[i-1][j]+1ll*f[i-1][j-1]*a[i])%P;
    			C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
    		}
    	}
    	for (int i=0;i<=n;i++) F[i]=f[n][i];
    	for (int i=1;i<=n;i++) G[i]=calc(1ll*i*ksm(n,k-1)%P,1ll*i*inv(n)%P,k);
    	for (int j=0;j<n;j++)
    		for (int x=1;x<=n-j;x++)
    		if (n-j-x&1) ans=(ans+P-1ll*C[n-j][x]*F[j]%P*G[x])%P;
    		else ans=(ans+1ll*C[n-j][x]*F[j]%P*G[x])%P;
    	ans=1ll*ans*inv(ksm(n,k))%P;
    	cout<<ans;
    	return 0;
    	//NOTICE LONG LONG!!!!!
    }
    

      回到原题意。注意到(我注意不到)每次res的增加量就是序列所有数乘积的减小量,于是最后要求的就是原序列乘积-修改后序列乘积的期望。当然对于期望统计所有方案最后除以方案数即可。

      显然如果第i个数减小了bi,方案数即为k!/∏bi!。此时所有数的乘积是∏(ai-bi)。最终要求Σbi=k时的∏(ai-bi)·k!/∏bi!。

      容易想到构造指数型生成函数,∏Σ(ai-j)xj/j!,k次项系数即为答案。

      上面这个式子等价于∏aiex-xex,拆开并泰勒公式还原显然可得。于是也就等于enx∏(ai-x)。

      这样我们分别算两部分即可。后一部分显然可以分治NTT,但模数不滋磁于是直接O(n2)暴力dp就好了。前一部分暴力展开,k次项系数为nk。答案就很显然了。注意是指数型生成函数,手动乘出第k项时还得乘一下。同样最后计算答案时也可以NTT,为啥要出到5000放个非NTT模数呢。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define N 5010
    #define P 1000000007
    char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
    int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
    int read()
    {
        int x=0,f=1;char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    int n,k,a[N],f[N][N],ans;
    int ksm(int a,int k)
    {
    	int s=1;
    	for (;k;k>>=1,a=1ll*a*a%P) if (k&1) s=1ll*s*a%P;
    	return s;
    }
    int inv(int a){return ksm(a,P-2);}
    void inc(int &x,int y){x+=y;if (x>=P) x-=P;}
    signed main()
    {
    #ifndef ONLINE_JUDGE
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        const char LL[]="%I64d
    ";
    #else
        const char LL[]="%lld
    ";
    #endif
    	n=read(),k=read();
    	for (int i=1;i<=n;i++) a[i]=read();
    	f[0][0]=1;
    	for (int i=1;i<=n;i++)
    	{
    		f[i][0]=1ll*f[i-1][0]*a[i]%P;
    		for (int j=1;j<=i;j++)
    		f[i][j]=(1ll*f[i-1][j]*a[i]-f[i-1][j-1]+P)%P;
    	}
    	int s=1;
    	for (int i=0;i<=min(k,n);i++)
    	{
    		inc(ans,1ll*ksm(n,k-i)*f[n][i]%P*s%P);
    		s=1ll*s*(k-i)%P;
    	}
    	ans=1ll*ans*inv(ksm(n,k))%P;
    	ans=(f[n][0]-ans+P)%P;
    	cout<<ans;
        return 0;
    }
    

      

  • 相关阅读:
    hdu 2222 Keywords Search
    Meet and Greet
    hdu 4673
    hdu 4768
    hdu 4747 Mex
    uva 1513 Movie collection
    uva 12299 RMQ with Shifts
    uva 11732 strcmp() Anyone?
    uva 1401
    hdu 1251 统计难题
  • 原文地址:https://www.cnblogs.com/Gloid/p/10458823.html
Copyright © 2011-2022 走看看