Vertex Covers(高維前綴和)

Vertex Covers

時間限制: 5 Sec  內存限制: 128 MB
提交: 5  解決: 3

題目描述

In graph theory, a vertex cover of a graph G is a set of vertices S such that each edge of the graph is incident to at least one vertex of the set. That is to say, for every edge (u,v) of the graph, either u or v is in the vertex cover S.
Now, Kamilah shows you an undirected graph G without loops or multiple edges, each vertex of which has a weight.She can evaluate a vertex cover S of G by the product of weights of all vertices belonging to S.Here, the product of an empty set (of numbers) is defined as 1.
You are asked to calculate the sum of the evaluations described above for all vertex covers of G.

輸入

The input contains several test cases, and the first line is a positive integer T indicating the number of test cases which is up to 3600.
For each test case, the first line contains three integers n (1≤n≤36) and m (0≤m≤n(n − 1)/2) which are the number of vertices and the number of edges in the graph G, and q (108≤q≤109 ) which is a prime number for the output.
The second line contains n integers, the i-th of which is the weight of the i-th vertices in G. All weights are in the range of 1 to 109 .
Each of the following m lines contains two integers u and v (1≤u, v≤n) describing an edge between the u-th vertex and the v-th one.
We guarantee that no more than 36 test cases satisfy n > 18.

輸出

For each test case, output a line containing Case #x: y, where x is the test case number starting from 1, and y is the remainder of the answer divided by q.

樣例輸入

2
4 3 998244353
1 1 1 1
1 2
2 3
3 4
4 6 998244353
1 1 1 1
1 2
1 3
1 4
2 3
2 4
3 4

樣例輸出

Case #1: 8
Case #2: 5

題意:設一個圖的點覆蓋的貢獻是該點覆集中點權的積,求一個圖的全部點覆蓋的貢獻和。
思路(來自題解):
c++

考慮折半,將點集分爲大小接近的兩部分 L 和 R,那麼邊集 分爲 L 內部的、R 內部的以及 L 和 R 之間的。
枚舉 L 的子集 S,檢查是否 L 內部全部邊都被覆蓋。
再枚舉 R 的子集 T,檢查是否 R 內部全部邊都被覆蓋,若是是,那麼根據 L 和 R 之間的未覆蓋邊能夠知道 L 的一個合法的子集 T′ 必需要覆蓋掉當前的未覆蓋邊,那麼能夠在 L 內選出所 有包含 T′ 的可行 S,這樣 S+T 就是一個 vertex cover。
因爲乘法知足分配率,只須要對 S 作一個高維前綴和就能 快速計算答案。ide

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
 
const int N = 37;
long long arr[N];
long long pre[1<<20]={0};
 
unsigned long long Map[N]={0};
 
int check(int x,int l,int r)
{
    for(int i=l;i<=r;i++)
    {
        if(((1<<i)&x)==0)
        {
            unsigned long long now=((Map[i]<<(63-r))>>(63-r));
            if((now&x)!=now)return 0;
        }
    }
    return 1;   
}
 
int check2(int x,int l,int r,int mid)
{
    for(int i=l;i<=r;i++)
    {
        if(((1<<i)&x)==0)
        {
            long long now=(Map[i+mid]>>mid);
            if((now&x)!=now)return 0;
        }
    }
    return 1;   
}
 
int main()
{
    int  t,Ca=1;
    scanf("%d",&t);
     
    while(t--)
    {
        int n,m;
        long long mod; 
        scanf("%d %d %lld",&n,&m,&mod);
         
        for(int i=0;i<n;i++)scanf("%lld",&arr[i]);
         
        memset(Map,0,sizeof(Map));
        while(m--)
        {
            int u,v;
            scanf("%d %d",&u,&v);
            u--;
            v--;
            Map[u]|=1ll<<v;
            Map[v]|=1ll<<u;
        }       
         
        int mid=n/2;
         
        for(int i=(1<<mid)-1;i>=0;i--)pre[i]=0;
         
        int upper=(1<<mid)-1;
         
        for(int i=0;i<=upper;i++)
        {
            long long sum=1;
            for(int j=0;j<mid;j++)
            if((1<<j)&i)sum=(sum*arr[j])%mod;
            
            if(check(i,0,mid-1))pre[i]=sum;
        }
         
        for(int i=0;i<mid;i++)   //高維後綴和 
        for(int j=upper;j>=0;j--)
            if((j&(1<<i))==0) pre[j]=(pre[j]+pre[j^(1<<i)])%mod;
         
        upper=(1<<(n-mid))-1;
         
        long long ans=0; 
        for(int i=0;i<=upper;i++)
        {
            long long sum=1;
            for(int j=0;j<n-mid;j++)
            if((1<<j)&i)sum=(sum*arr[j+mid])%mod;
             
            if(check2(i,0,n-mid-1,mid))
            {
                long long base=0;
                for(int x=0;x<=mid-1;x++)
                {
                    unsigned long long now=(Map[x]>>mid); 
                    if((now&i)!=now)base|=1<<x;
                }
                ans=(ans+sum*pre[base]%mod)%mod;
            } 
        }
        printf("Case #%d: %lld\n",Ca++,ans);
    }
    return 0;
}
View Code
相關文章
相關標籤/搜索