• bzoj5153&uoj348 【WC2018】州区划分


    五十分就是裸的O(3^n)子集dp。

    $$f[S]*{w[S]^{p}}=sum_{T in S}{f[T]*{w[S-T]^{p}}}$$ 

    然后我们考虑优化这个dp,我们发现这是子集卷积的形式,于是我们就可以用fwt来优化这个dp。

    具体的,f[i][S]表示的是S的f值,当且仅当S中1的个数为i,别的f[i][S1]不正确也没有问题,因为我们转移时枚举的是1的个数,所以只有正确的转移会作出正确的贡献。然后就是一个裸的或or异或的fwt。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 #include <algorithm>
      5 #include <cmath>
      6 #define N 2222222
      7 #define mod 998244353
      8 using namespace std;
      9 int can[N],g[N],inv[N],cnt[N],f[25][N],h[25][N];
     10 int n,m,p,to[25],w[25];
     11 int e=1,head[25],fa[25];
     12 struct edge{
     13     int v,next;
     14 }ed[2333];
     15 void add(int u,int v){
     16     ed[e].v=v;ed[e].next=head[u];head[u]=e++;
     17 }
     18 int find(int x){
     19     return x==fa[x]?x:fa[x]=find(fa[x]);
     20 }
     21 int qp(int a,int b){
     22     int c=1;
     23     while(b){
     24         if(b&1)c=1ll*c*a%mod;
     25         a=1ll*a*a%mod; b>>=1;
     26     }
     27     return c;
     28 }
     29 void UPD(int &a,int b){
     30     a=(a+b>=mod)?(a+b-mod):(a+b);
     31 }
     32 void IPD(int &a,int b){
     33     a=(a-b<0)?(a-b+mod):(a-b);
     34 }
     35 bool check(int s){
     36     if(cnt[s]==1)return 0;
     37     int a[25],num=0;
     38     for(int i=1;i<=n;i++)if(s&(1<<i-1)){
     39         a[++num]=i;
     40         int now=s&to[i];
     41         if(!now)return 1;
     42         if(cnt[now]&1)return 1;
     43     }
     44     for(int i=1;i<=num;i++)fa[a[i]]=a[i];
     45     for(int i=1;i<=num;i++){
     46         for(int j=head[a[i]];j;j=ed[j].next)if(s&(1<<ed[j].v-1))
     47             if(find(a[i])!=find(ed[j].v))
     48                 fa[find(a[i])]=find(ed[j].v);
     49     }
     50     for(int i=2;i<=num;i++)
     51         if(find(a[i])!=find(a[1]))return 1;
     52     return 0;
     53 }
     54 int calc(int s){
     55     if(!p)return 1;
     56     int ans=0;
     57     for(int i=1;i<=n;i++)if(s&(1<<i-1))
     58         UPD(ans,w[i]);
     59     if(p==1)return ans;
     60     return 1ll*ans*ans%mod;
     61 }
     62 void fwt(int *a){
     63     for(int k=2;k<=(1<<n);k<<=1)
     64         for(int i=0;i<(1<<n);i+=k)
     65             for(int j=0;j<k>>1;j++)
     66                 UPD(a[i+j+(k>>1)],a[i+j]);
     67 }
     68 
     69 void ifwt(int *a){
     70     for(int k=2;k<=(1<<n);k<<=1)
     71         for(int i=0;i<(1<<n);i+=k)
     72             for(int j=0;j<k>>1;j++)
     73                 IPD(a[i+j+(k>>1)],a[i+j]);
     74 }
     75 int main(){
     76     scanf("%d%d%d",&n,&m,&p);
     77     for(int i=0;i<(1<<n);i++)cnt[i]=cnt[i>>1]+(i&1);
     78     for(int i=1,u,v;i<=m;i++){
     79         scanf("%d%d",&u,&v);
     80         to[u]|=(1<<v-1);
     81         to[v]|=(1<<u-1);
     82         add(u,v);add(v,u);
     83     }
     84     for(int i=1;i<=n;i++)scanf("%d",&w[i]);
     85     for(int i=1;i<(1<<n);i++){
     86         can[i]=check(i);
     87         g[i]=calc(i);
     88         inv[i]=qp(g[i],mod-2);
     89     }
     90     for(int i=1;i<(1<<n);i++)
     91         h[cnt[i]][i]=can[i]*g[i];
     92     for(int i=1;i<=n;i++)fwt(h[i]);
     93     f[0][0]=1;fwt(f[0]);
     94     for(int i=1;i<=n;i++){
     95         for(int j=0;j<i;j++)
     96             for(int k=0;k<(1<<n);k++)
     97                 UPD(f[i][k],1ll*f[j][k]*h[i-j][k]%mod);
     98         ifwt(f[i]);
     99         for(int j=0;j<(1<<n);j++)
    100             f[i][j]=1ll*f[i][j]*inv[j]%mod;
    101         if(i^n)fwt(f[i]);
    102     }
    103     printf("%d
    ",f[n][(1<<n)-1]);
    104     return 0;
    105 }
    View Code
  • 相关阅读:
    《剑指offer》面试题7—用两个栈实现队列
    《剑指offer》面试题17—合并两个排序链表
    《剑指offer》面试题16—反转链表
    《剑指offer》面试题15—输出链表中倒数第n个结点
    《剑指offer》面试题13—O(1)时间删除链表结点
    《剑指offer》面试题5—从尾到头打印链表
    C++虚继承作用
    C++多态性:虚函数的调用原理
    《剑指offer面试题4》替换空格——实现函数把字符串中每个空格替换成“%20”
    《剑指offer》面试题1:为类CMyString添加赋值运算符函数——C++拷贝构造函数与赋值函数
  • 原文地址:https://www.cnblogs.com/Ren-Ivan/p/8877829.html
Copyright © 2020-2023  润新知