@codeforces - グラフと数字@ 1221G


@説明@

点n mの無向グラフのエッジ与えられます。

各点は、現在、二時の重みの右側の定義の値を0または1を書き込むために必要とエッジによって接続されています。

右どのように多くのプログラム、例えば少なくとも片側の存在は0、少なくとも一方の側の重量が1、2の重量を有する少なくとも一つの側面であること。

入力
最初の行は、2つの数値が含まれ、nおよびm(1≤n≤40、0≤m≤n×(N -1)/ 2)は、 点とエッジの数を表します。
二つの整数Xi及びYI(1≤xi、yi≤n、XI≠次のm行 YI)は、 エッジの両端点を記載しています。
ノー重い側面ことを確実にするために。

出力
出力法的プログラムの数。

実施例の
入力
6 5
1 2
2 3
3 4
4 5
5 1
出力
20

入力
4 4
1 2
2 3
3 4
4 1
出力
4

@解決@

N <= 40は、途中でその会うことを意味します。要件が包含排除カウント方式を意味します。

:まず、インクルージョン-除外は、我々は次のシナリオの数数える方法を検討
F1:制限はありませんが。
F2:フォース0は表示されません。
F3:1が強制的に表示されません。
F4:2表示されませんを余儀なくされました。
F5:0と1が表示されるように強制されていません。
F6:0と2が表示されるように強制されていません。
F7:1強制と2は表示されません。
F8:フォース0、1、2は表示されません。
最終的な答えANS = F1 - F2 - F3 - F4 + F5 + F6 + F7 - F8。

次に、どのように特定の数を考えます。
F1 = 2 ^ nはまず、容易に入手できます。
すべての接続された構成要素のサイズ、連結成分のm個の合計である場合、F8 = 2 ^ M; = 0それ以外の場合F8。
接続されたすべての構成要素が二部グラフであれば、接続された構成要素は、次いで、F6 = 2 ^ Mがmが、0 =それ以外の場合はF6。
サイズは、連結成分の数が1つの場合、Kは、= F5、F7 = 2 ^ Kです。
もしM、F3 = 2 ^ mと接続されたコンポーネントの合計数。

残りF2ながら、F4は、双方向の検索を必要としています。F2 = F4ので、我々は唯一のF4を考えるかもしれません。
点uは0の値を有する場合、それは限定されないが、隣接するドットは、Uのポイント値が1である場合にそうでなければ、それは0だけ隣接点です。

我々はゼロに到達するためにポイントして、[現在の状態が正当なものであるかを決定しなければならないバイナリ状態、前半を列挙します。
注F [S] S合法、1の合法性、そうでなければ0を表し、G [S]バイナリS '<= S Fの[S']和を表し、かつ高次元の接頭辞です。

バイナリ状態列挙のクエリ半分、第0又は1であり、その後の最初の半分は0正当である得るかどうかを決定します。値gへのダイレクトアクセス。

@acceptedコード@

#include<cstdio>
#include<queue>
using namespace std;
typedef long long ll;
int n, m; ll G[40 + 5];
int fa[40 + 5], siz[40 + 5];
int find(int x) {
    return fa[x] = (fa[x] == x ? x : find(fa[x]));
}
void unite(int x, int y) {
    int fx = find(x), fy = find(y);
    if( fx != fy ) fa[fx] = fy, siz[fy] += siz[fx];
}
ll check(int x) {
    ll d[40 + 5] = {};
    for(int i=0;i<n;i++)
        d[i] = -1;
    queue<int>que; que.push(x); d[x] = 0;
    while( !que.empty() ) {
        int f = que.front(); que.pop();
        for(int i=0;i<n;i++)
            if( (G[f]>>i) & 1 ) {
                if( d[i] == -1 ) {
                    d[i] = d[f] ^ 1;
                    que.push(i);
                }
                else {
                    if( d[i] != (d[f] ^ 1) )
                        return 0;
                }
            }
    }
    return 2;
}
ll solve1() {
    ll ret1 = 1, ret2 = 1, ret3 = 1, ret4 = 1;
    for(int i=0;i<n;i++)
        if( fa[i] == i ) {
            ret1 <<= 1;
            ret2 = ret2*check(i);
            ret3 = (siz[i] == 1) ? (ret3<<1) : ret3;
            ret4 = (siz[i] == 1) ? (ret4<<1) : 0;
        }
    return ret2 - ret1 + (ret3<<1) - ret4;
}
ll f[1<<20];
void dfs1(int d, int mxd, int s1, int s2) {
    if( d == mxd ) {
        f[s1]++;
        return ;
    }
    dfs1(d + 1, mxd, s1, s2);
    if( !(s2 & (1LL<<d)) )
        dfs1(d + 1, mxd, s1|(1LL<<d), s2|G[d]);
}
int mid, t;
ll dfs2(int d, int mxd, ll s) {
    if( d == mxd ) {
        s = s & t, s = s ^ t;
        return f[s];
    }
    ll ret = dfs2(d + 1, mxd, s);
    if( !(s & (1LL<<d)) )
        ret += dfs2(d + 1, mxd, s|G[d]);
    return ret;
}
ll solve2() {
    mid = (n>>1), t = (1<<mid) - 1;
    dfs1(0, mid, 0, 0);
    for(int i=0;i<mid;i++)
        for(int j=0;j<=t;j++)
            if( j & (1LL<<i) ) f[j] += f[j^(1LL<<i)];
    return dfs2(mid, n, 0);
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i=0;i<n;i++) fa[i] = i, siz[i] = 1;
    for(int i=1;i<=m;i++) {
        int x, y; scanf("%d%d", &x, &y), x--, y--;
        G[x] |= (1LL<<y), G[y] |= (1LL<<x), unite(x, y);
    }
    printf("%lld\n", (1LL<<n) + solve1() - (solve2()<<1));
}

@詳細@

アカウントに、WA一度ブロック(すなわち、側ブロックを通信していない)ケースの大きさを取ることなく、通信開始。

それ以来、長い長い、WA数回開くことができませんでした。

おすすめ

転載: www.cnblogs.com/Tiw-Air-OAO/p/11568191.html