Luogu4221 WC2018州区划分(状压dp+FWT)

  合法条件为所有划分出的子图均不存在欧拉回路或不连通,也即至少存在一个度数为奇数的点或不连通。显然可以对每个点集预处理是否合法,然后就不用管这个奇怪的条件了。

  考虑状压dp。设f[S]为S集合所有划分方案的满意度之和,枚举子集转移,则有f[S]=Σg[S‘]*f[S^S‘]*(sum[S‘]/sum[S])(S‘?S),其中g[S]为S集合是否合法,sum[S]为S集合人口数之和。复杂度O(3n)。这个式子非常显然,就这么送了50分。p这么小显得非常奇怪但也没有任何卵用。

  考虑优化。转移方程写的更优美一点大约是f[S]=Σf[x]*g[y]/h[S] (x|y=S,x&y=0)。看起来像是一个或卷积,但还有后面一个限制。考虑在x|y=S的前提下,x&y=0实际上相当于|x|+|y|=|S|。于是稍微改一下状态,f[i][S]为i个点所选点集为S时的满意度之和(虽然第一维显然是可以由第二维推出的),g同样更改状态,这样转移就是f[i][S]=Σf[u][x]*g[v][y]/h[S] (x|y=S,u+v=i)。暴力枚举第一维u,FWT做或卷积即可,复杂度O(2n·n2)。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 21
#define P 998244353
#define rep(i,t,S) for (int t=S,i=lg2[t&-t];t;t^=t&-t,i=lg2[t&-t])
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,p,a[N][N],w[N],fa[N],degree[N],lg2[1<<N],sum[1<<N],size[1<<N],f[N+1][1<<N],g[N+1][1<<N];
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 find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void inc(int &x,int y){x+=y;if (x>=P) x-=P;}
void get()
{
    for (int i=0;i<n;i++) lg2[1<<i]=i;
    for (int i=1;i<(1<<n);i++)
    {
        sum[i]=sum[i^(i&-i)]+w[lg2[i&-i]];
        size[i]=size[i^(i&-i)]+1;
        rep(x,u,i) fa[x]=x,degree[x]=0;
        rep(x,u,i)
        {
            rep(y,v,i) if (a[x][y]) degree[x]^=1,fa[find(x)]=find(y);
            if (degree[x]) {g[size[i]][i]=1;break;}
        }
        int f=-1;
        rep(x,u,i) if (f==-1) f=find(x);else if (f!=find(x)) {g[size[i]][i]=1;break;}
    }
    for (int i=0;i<(1<<n);i++) sum[i]=ksm(sum[i],p),g[size[i]][i]*=sum[i];
}
void FWT(int *a,int n,int op)
{
    for (int i=2;i<=n;i<<=1)
        for (int j=0;j<n;j+=i)
            for (int k=j;k<j+(i>>1);k++)
            if (!op) a[k+(i>>1)]=(a[k+(i>>1)]+a[k])%P;
            else a[k+(i>>1)]=(a[k+(i>>1)]-a[k]+P)%P;
}
void solve()
{
    f[0][0]=1;
    FWT(f[0],1<<n,0);
    for (int i=0;i<=n;i++) FWT(g[i],1<<n,0);
    for (int i=1;i<=n;i++)
    {
        for (int j=0;j<(1<<n);j++)
            for (int x=0;x<i;x++)
            f[i][j]=(f[i][j]+1ll*f[x][j]*g[i-x][j])%P;
        FWT(f[i],1<<n,1);
        for (int j=0;j<(1<<n);j++)
        if (size[j]==i) f[i][j]=1ll*f[i][j]*inv(sum[j])%P;
        FWT(f[i],1<<n,0);
    }
    FWT(f[n],1<<n,1);
    cout<<f[n][(1<<n)-1];
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),m=read(),p=read();
    for (int i=1;i<=m;i++)
    {
        int x=read()-1,y=read()-1;
        a[x][y]=a[y][x]=1;
    }
    for (int i=0;i<n;i++) w[i]=read();
    get();
    solve();
    return 0;
}
 

原文地址:https://www.cnblogs.com/Gloid/p/10268671.html

时间: 2024-08-27 05:10:30

Luogu4221 WC2018州区划分(状压dp+FWT)的相关文章

[UOJ#348][WC2018]州区划分

[UOJ#348][WC2018]州区划分 试题描述 小 \(S\) 现在拥有 \(n\) 座城市,第ii座城市的人口为 \(w_i\),城市与城市之间可能有双向道路相连. 现在小 \(S\) 要将这 \(n\) 座城市划分成若干个州,每个州由至少一个城市组成,每个城市在恰好一个州内. 假设小 \(S\) 将这些城市划分成了 \(k\) 个州,设 \(V_i\) 是第 \(i\) 个州包含的所有城市组成的集合. 定义一条道路是一个州的内部道路,当且仅当这条道路的两个端点城市都在这个州内. 如果一

HDU 5765 Bonds(状压DP)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5765 [题目大意] 给出一张图,求每条边在所有边割集中出现的次数. [题解] 利用状压DP,计算不同的连通块,对于每条边,求出两边的联通块的划分方案数,就是对于该点的答案. [代码] #include <cstdio> #include <algorithm> #include <cstring> using namespace std; int n,m,T,Cas=1

poj2411 Mondriaan&#39;s Dream【状压DP】

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 20822   Accepted: 11732 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet serie

『Exclusive Access 2 dilworth定理 状压dp』

Exclusive Access 2 Description 给出 N 个点M 条边的无向图,定向得到有向无环图,使得最长路最短. N ≤ 15, M ≤ 100 Input Format 第一行一个数M (1≤M≤100). 接下来M行,每行两个大写字母(L 到 Z),最多出线15个不同的大写字母.每行的两个大写字母不会相同. Output Format 第一行输出最长路最短的数值-1. Sample Input 3 P Q Q R R P Sample Output 1 解析 二分答案?想多

ZOJ3305Get Sauce 状压DP,

状压DP的题目留个纪念,首先题意一开始读错了,搞了好久,然后弄好了,觉得DFS可以,最后超时,修改了很久还是超时,没办法看了一下n的范围,然后觉得状压可以,但是没有直接推出来,就记忆化搜索了一下,可是一直错,莫名奇妙,然后没办法看了一下题解,发现了下面这个比较好的方法,然后按照这个方程去推,然后敲,也是WA了好多把,写的太搓了,没人家的清楚明了,唉~也算是给自己留个纪念,状压一直做的都不太好~唉~还好理解了, 参考了  http://blog.csdn.net/nash142857/articl

poj 2411 Mondriaan&#39;s Dream(状压DP)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12232   Accepted: 7142 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

(状压dp)uva 10817 Headmaster&#39;s Headache

题目地址 1 #include <bits/stdc++.h> 2 typedef long long ll; 3 using namespace std; 4 const int MAX=1e5+5; 5 const int INF=1e9; 6 int s,m,n; 7 int cost[125]; 8 //char sta[MAX]; 9 string sta; 10 int able[125]; 11 int dp[125][1<<8][1<<8]; 12 in

HDU5816 Hearthstone(状压DP)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5816 Description Hearthstone is an online collectible card game from Blizzard Entertainment. Strategies and luck are the most important factors in this game. When you suffer a desperate situation an

HDU 4336 容斥原理 || 状压DP

状压DP :F(S)=Sum*F(S)+p(x1)*F(S^(1<<x1))+p(x2)*F(S^(1<<x2))...+1; F(S)表示取状态为S的牌的期望次数,Sum表示什么都不取得概率,p(x1)表示的是取x1的概率,最后要加一因为有又多拿了一次.整理一下就可以了. 1 #include <cstdio> 2 const int Maxn=23; 3 double F[1<<Maxn],p[Maxn]; 4 int n; 5 int main() 6