6.28联考题解

这篇拖得有点久…

A:
模拟我们做kmp的过程,我们会得到两类关系,一类关系是第 i 个位置和第 j 个位置相等,另一类是第 i 个位置和第 j 个位置不等,相等的我们可以把他们合并在一起,于是变成一个图,相邻点不同色,共有 c 种颜色,求总染色个数

这类图染色问题只有弦图是能做的否则做不了
他这个kmp的过程似乎加的点就是完美消除序列
证明的话可以考虑假设新加的点相连的两个点 x y 之间没有边,然后在图上画一下可以发现根据kmp的性质他们一定会被合并成一个点

得到这个性质后只要每次加点时染色方案数*=( c 该点的度数)就行了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 210000;
const int mod  = 1e9+7;

int n,C,ans;
int fa[maxn],Ti[maxn];
int findfa(const int x){ return fa[x]==x?x:fa[x]=findfa(fa[x]); }

int f[maxn];

int main()
{
    freopen("kmp.in","r",stdin);
    freopen("kmp.out","w",stdout);

    scanf("%d%d",&n,&C); ans=C;
    fa[1]=1;
    for(int i=2,k=0;i<=n;k=f[i],i++)
    {
        scanf("%d",&f[i]);
        if(f[i]!=0) fa[i]=findfa(f[i]);
        else
        {
            int sum=0;
            while(1)
            {
                int tmp=findfa(k+1);
                if(Ti[tmp]!=i) sum++,Ti[tmp]=i;
                if(!k) break;
                k=f[k];
            }
            ans=(ll)ans*(C-sum)%mod;
            fa[i]=i;
        }
    }
    printf("%d\n",ans);

    return 0;
}

B:
n个数所有子集异或和中1的个数的和
对这n个数求个线性基,设线性基中有 k 个数
线性基中的数可以通过互相异或,使得每个数在这 k 位只有他的那一位是1,其他位都是0
那么这 k 位中的某一位如果要有1,一定要异或上对应的这个数
k <= 25 时,可以直接 2 k 枚举子集
k > 25 时,注意到剩下的位 <= 14 个,我们可以把基中的 k 个位映射到最高的 k 个位,剩下的就是二进制最低的40-k个位,然后可以做个dp
f [ i ] [ j ] [ k ] 表示dp到线性基的第 i 个数,之前的数在最高的 k 位有 j 个1,剩下的位异或和为 k 的方案数
然后可以算出 n u m [ x ] 表示异或和有 x 个1的方案数

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 45;
const int maxb = 39;
const int mod  = 1e9+7;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;}

int n,K,zero;
ll a[maxn],base[maxb+10];
int t[maxn],tp;

int num[maxb+10];
namespace Subtask1
{
    void dfs(const int i,const ll now)
    {
        if(i>tp)
        {
            num[__builtin_popcountll(now)]++;
            return;
        }
        dfs(i+1,now);
        dfs(i+1,now^base[t[i]]);
    }
    void main() { dfs(1,0); }
}
namespace Subtask2
{
    int f[2][maxn][1<<16];
    int trans[maxb+10],nt[maxb+10];
    void main()
    {
        memset(trans,-1,sizeof trans);

        int cnt=maxb;
        for(int i=1;i<=tp;i++) trans[t[i]]=cnt,nt[cnt]=t[i],cnt--;
        int al=1<<(cnt+1);
        for(int b=0;b<=maxb;b++) if(trans[b]==-1) trans[b]=cnt--;

        for(int ii=1;ii<=tp;ii++)
        {
            int i=t[ii]; ll c=0;
            for(int b=0;b<=maxb;b++) if(base[i]>>b&1ll)
                c|=1ll<<trans[b];
            base[i]=c;
        }
        for(int i=1;i<tp;i++)
        {
            int x=nt[maxb-i+1];
            for(int j=i+1;j<=tp;j++) if(base[x]>>(maxb-j+1)&1ll)
            {
                int y=nt[maxb-j+1];
                base[x]^=base[y];
            }
        }

        int now=0; f[now][0][0]=1;
        ll Al=al-1;
        for(int ii=1;ii<=tp;ii++)
        {
            int i=t[ii]; now=!now;
            int c=(base[i]&Al);
            for(int k=0;k<ii;k++) for(int j=0;j<al;j++) if(f[!now][k][j])
            {
                int &temp=f[!now][k][j];
                add(f[now][k][j],temp);
                add(f[now][k+1][j^c],temp);
                temp=0;
            }
        }
        for(int k=0;k<=tp;k++) for(int j=0;j<al;j++) if(f[now][k][j])
            add(num[k+__builtin_popcount(j)],f[now][k][j]);
    }
}

int pw(int x,int k)
{
    int re=1;
    for(;k;k>>=1,x=(ll)x*x%mod) if(k&1)
        re=(ll)re*x%mod;
    return re;
}
int cal()
{
    int ans=0,k=pw(2,zero);
    for(int i=0;i<=maxb+1;i++)
        add(ans,(ll)num[i]*k%mod*pw(i,K)%mod);
    return ans;
}

int main()
{
    freopen("xor.in","r",stdin);
    freopen("xor.out","w",stdout);

    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)
    {
        int use=0;
        for(int b=maxb;!use&&b>=0;b--) if(a[i]>>b&1ll)
        {
            if(base[b]) a[i]^=base[b];
            else base[b]=a[i],use=1;
        }
        if(!use) zero++;
    }
    for(int b=maxb;b>=0;b--) if(base[b]) t[++tp]=b;

    if(tp<=25) Subtask1::main();
    else Subtask2::main();

    printf("%d\n",cal());

    return 0;
}

C:
好像前几年冬令营讲圆方树的那个课件讲了这道题
大概是弄个状压,然后把树缩一下缩剩 O ( m n ) 个点,然后复杂度就能做到 O ( ( m n ) 2 m n )
我不太会qaq

猜你喜欢

转载自blog.csdn.net/L_0_Forever_LF/article/details/80903840
今日推荐