【学习笔记】[ABC306Ex] Balance Scale

速速让我退役

首先要做过这道题 [CEOI2019] Amusement Park 。

然后发现上面那道题的做法直接套是不行的,因为有情况 3 3 3。枚举拆分数肯定是没前途的,这个想都不用想。我还真想了。

仔细思考发现这道题目几乎等价于无向图边定向的 D A G DAG DAG计数。唯一的区别是枚举的入度为 0 0 0的集合似乎不再要求是一个简单的独立集了。

发现问题出在容斥系数上面。怎么改就对了?我们发现把独立集的大小看成连通块的数目就好了。

证明后面再补。这题出的挺好的,考察的角度比较深入。

复杂度 O ( 3 n + 2 n × n 2 ) O(3^n+2^n\times n^2) O(3n+2n×n2)

remark \text{remark} remark 说实话还是观察了很久才胡出这个结论。

#include
#define fi first
#define se second
#define ll long long
#define pb push_back
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
using namespace std;
const int mod=998244353;
ll fpow(ll x,ll y=mod-2){
    ll z(1);
    for(;y;y>>=1){
        if(y&1)z=z*x%mod;
        x=x*x%mod;
    }return z;
}
void add(ll &x,ll y){
    x=(x+y)%mod;
}
int lowbit(int x){return x&-x;}
int n,m,U[1005],ct[1<<17],V[1005],fa[17];
ll f[1<<17];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;for(int i=0;i<m;i++)cin>>U[i]>>V[i],U[i]--,V[i]--;
    for(int i=1;i<1<<n;i++){
        for(int j=0;j<n;j++){
            if(i>>j&1)fa[j]=j,ct[i]++;
        }
        for(int j=0;j<m;j++){
            if((i>>U[j]&1)&&(i>>V[j]&1)&&find(U[j])!=find(V[j]))fa[fa[U[j]]]=fa[V[j]],ct[i]--;
        }
    }
    f[0]=1;
    for(int s=1;s<1<<n;s++){
        for(int s2=s;s2;s2=(s2-1)&s){
            if(ct[s2]&1)add(f[s],f[s-s2]);
            else add(f[s],-f[s-s2]);
        }
    }
    cout<<(f[(1<<n)-1]+mod)%mod;
}

你可能感兴趣的:(学习,笔记,算法)