省选模拟赛 让苍天知道我不认输(40分)

分析:强大的bitset......

   对于每一个点i,其bitset f[i][j]表示f(t(i -> j)). 枚举两个点i,j. 如果f[i][j] = 1. 那么f[i] & f[j]中1的个数就是i,j的贡献. 否则f[i]和f[j]相同位置处0的个数就是贡献. 先以每个点为起点dfs一次就能预处理出f了.

   用bitset主要是因为想确定i,j连到的是不是同一个点. 直接求方案数可能不合法.

#include <cstdio>
#include <bitset>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 1010;
int n,y,k,r,a[maxn],head[maxn],to[maxn * 2],nextt[maxn * 2],tot = 1,vis[maxn];
bitset <maxn> f[maxn],b,temp1,temp2,temp;
long long ans;

void add(int x,int y)
{
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;
}

void dfs(int S,int u,int z,int ki)
{
    int temp = (z + a[u] * ki % y) % y;
    if (temp == r)
        f[S][u] = 1;
    else
        f[S][u] = 0;
    vis[u] = 1;
    for (int i = head[u];i;i = nextt[i])
    {
        int v = to[i];
        if (!vis[v])
        {
            int tmp = ki * k % y;
            dfs(S,v,temp,tmp);
        }
    }
}

int main()
{
    scanf("%d%d%d%d",&n,&y,&k,&r);
    for (int i = 1; i <= n; i++)
        scanf("%d",&a[i]);
    for (int i = 1; i < n; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    for (int i = 1; i <= n; i++)
    {
        memset(vis,0,sizeof(vis));
        dfs(i,i,0,1);
    }
    for (int i = 1; i <= n; i++)
        b[i] = 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (f[i][j] == 1)
            {
                temp = f[i] & f[j];
                ans += temp.count();
            }
            else
            {
                temp1 = f[i] ^ b;
                temp2 = f[j] ^ b;
                temp = temp1 & temp2;
                ans += temp.count();
            }
        }
    }
    printf("%lld\n",ans);

    return 0;
}
posted @ 2018-04-04 14:38 zbtrs 阅读(...) 评论(...) 编辑 收藏