这篇拖得有点久…
A:
模拟我们做kmp的过程,我们会得到两类关系,一类关系是第 i i 个位置和第 j j 个位置相等,另一类是第 i i 个位置和第 j j 个位置不等,相等的我们可以把他们合并在一起,于是变成一个图,相邻点不同色,共有 c c 种颜色,求总染色个数
这类图染色问题只有弦图是能做的否则做不了
他这个kmp的过程似乎加的点就是完美消除序列
证明的话可以考虑假设新加的点相连的两个点 x x , y y 之间没有边,然后在图上画一下可以发现根据kmp的性质他们一定会被合并成一个点
得到这个性质后只要每次加点时染色方案数*=( c− c − 该点的度数)就行了
code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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 个数
线性基中的数可以通过互相异或,使得每个数在这 k k 位只有他的那一位是1,其他位都是0
那么这 k k 位中的某一位如果要有1,一定要异或上对应的这个数
当 k<=25 k <= 25 时,可以直接 2k 2 k 枚举子集
当 k>25 k > 25 时,注意到剩下的位 <=14 <= 14 个,我们可以把基中的 k k 个位映射到最高的 k k 个位,剩下的就是二进制最低的40-k个位,然后可以做个dp
设 f[i][j][k] f [ i ] [ j ] [ k ] 表示dp到线性基的第 i i 个数,之前的数在最高的 k k 位有 j j 个1,剩下的位异或和为 k k 的方案数
然后可以算出 num[x] n u m [ x ] 表示异或和有 x x 个1的方案数
code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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<for(int i=1;iint 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;kfor(int j=0;jif(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;jif(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 ) 个点,然后复杂度就能做到 O((m−n)2m−n) O ( ( m − n ) 2 m − n )
我不太会qaq