• [NOI Online #3 提高组]优秀子序列


    desciption

    solution:

    这道题得一步一步来
    直接求答案肯定不好求,思考怎样将答案进行分类
    观察到答案奇怪的形式,显然是让我们根据优秀子序列的和来分类
    于是我们记(f_{s})表示优秀子序列和为s时有多少中方案
    那么最终答案就是(ans=sum_sf_s*phi (s+1))
    然后我们发现这个看似定义得很随意的状态竟然和题目惊人地契合
    考虑增量地构造一个优秀子序列,设其和为s(显然一个本就优秀的子序列去掉其中任何一个数后仍然是优秀的)
    设将要加入的数为p
    p能够加入当且仅当(p)&(s==0)
    而如果数p加入后,(f_s)就恰好能对(f_{s+p})产生一个新的贡献
    那么状态转移方程就不难想到了:(f_s=sum f_{s-t}*cnt_t)
    其中(cnt_t)为t在原序列中出现的次数,(t)&(s==0)
    边界情况是(f_0=2^{cnt_0})
    然后就可以dp了
    需要注意的是如果直接dp可能会重复(比如(1,2)和(2,1)会被计算两次)
    于是我们可以从小到大地遍历序列中的数,然后对于每一个数统计它对答案的贡献

    code:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+5,M=(1<<18)+5,mod=1e9+7;
    int cnt[N],f[M],n,w;
    inline int read()
    {
    	int s=0,w=1; char ch=getchar();
    	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
    	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
    	return s*w;
    }
    int phi[M];
    vector<int>p;
    bool flag[M];
    inline void pre(int lim)
    {
    	phi[1]=1;flag[1]=0;
    	for(int i=2;i<=lim;++i)
    	{
    		if(!flag[i])p.push_back(i),phi[i]=i-1;
    		for(int j=0;j<p.size();++j)
    		{
    			if(i*p[j]>lim)break;
    			flag[i*p[j]]=true;
    			if(i%p[j])phi[i*p[j]]=phi[i]*phi[p[j]];
    			else{phi[i*p[j]]=phi[i]*p[j];break;}
    		}
    	}
    }
    inline int qpow(int x,int y)
    {
    	int ans=1;
    	for(;y;y>>=1,x=1ll*x*x%mod)
    		if(y&1)ans=1ll*ans*x%mod;
    	return ans;
    }
    int main()
    {
    	n=read();
    	int mx=0;
    	for(int i=1;i<=n;++i)
    	{
    		int x=read();++cnt[x];
    		mx=max(mx,x);
    	}
    	w=(int)log2(mx)+1;
    	pre(1<<w);f[0]=qpow(2,cnt[0]);
    	int p=0;
    	for(int i=1;i<=mx;++i)
    		if(cnt[i])
    		{
    			p|=i;int s=p^i;
    			for(int t=s;;t=(t-1)&s)
    			{
    				f[t|i]=(f[t|i]+1ll*f[t]*cnt[i]%mod)%mod;
    				if(!t)break;
    			}
    		}
    	int ans=0;
    	for(int i=0;i<(1<<w);++i)
    		ans=(ans+1ll*f[i]*phi[i+1]%mod)%mod;
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    AMF序列化技巧
    为什么用ByteArray序列化的对象如此小?
    解决Asp.net中翻页问题的自定义用户控件
    新建对象:反射会调用构造函数,clone不会调用构造函数
    Java 的传值小例子
    JDK中设计模式
    tryfinally中的返回值
    c++类中的常量(注意)
    创建有个性的对话框之MFC篇(转)
    用VC在IE浏览器的工具条上添加命令按钮(转 可以借鉴)
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/13833041.html
Copyright © 2020-2023  润新知