HDU 5739 Fantasia

可以将这个图转换成森林来进行树形dp求解。看了这篇具体教学才会的:http://www.cnblogs.com/WABoss/p/5696926.html

大致思路:求解一下点双连通分量(Tarjan),新构造一个节点连向这个分量中每一个节点。每个点双连通分量都这样构造好之后,原本连通的一张图就形成了一棵树,并且这个树中拿掉一个节点之后的连通性和原图相同!因此,可以在树上求解答案(树dp即可)。

注意:数据不一定保证原图是连通的,所以要特别注意原图中单个点的情况。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;
void File()
{
    freopen("D:\\in.txt","r",stdin);
    freopen("D:\\out.txt","w",stdout);
}
inline int read()
{
    char c = getchar();  while(!isdigit(c)) c = getchar();
    int x = 0;
    while(isdigit(c)) { x = x * 10 + c - ‘0‘; c = getchar(); }
    return x;
}

const int maxn=200010;
const LL mod=1e9+7;

int h[maxn],tot;
struct X { int u,v,nx; }e[2*maxn];

int pre[maxn],dfs_clock,low[maxn],bcc_cnt,bccno[maxn];

struct Edge
{
    int u,v;
    Edge(int from,int to) { u=from; v=to; }
};
stack<Edge> S;
vector<int>bcc[maxn];

int T,n,m,be[2*maxn],block,sz,cnt[maxn];
bool f[maxn];
LL w[2*maxn],ans[maxn],val[maxn],SUM,pru[2*maxn];

int head[2*maxn];
struct Tree{ int u,v,nx; }tree[4*maxn];
int id;

LL extend_gcd(LL a,LL b,LL &x,LL &y)
{
    if(a==0&&b==0) return -1;
    if(b==0){x=1;y=0;return a;}
    LL d=extend_gcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}

LL mod_reverse(LL a)
{
    LL x,y;
    LL d=extend_gcd(a,mod,x,y);
    if(d==1) return (x%mod+mod)%mod;
    else return -1;
}

void ADD(int a,int b)
{
    tree[tot].u=a, tree[tot].v=b, tree[tot].nx=head[a], head[a]=tot++;
}

void AddEdge(int a,int b)
{
    e[tot].u=a, e[tot].v=b, e[tot].nx=h[a], h[a]=tot++;
}

int Tarjan(int u,int fa)
{
    int lowu=pre[u]=++dfs_clock;
    for(int i=h[u];i!=-1;i=e[i].nx)
    {
        int v=e[i].v;
        Edge e = Edge(u,v);
        if(!pre[v])
        {
            S.push(e);
            int lowv=Tarjan(v,u); lowu=min(lowv,lowu);
            if(lowv>=pre[u])
            {
                bcc_cnt++; id++; w[id]=1;
                for(;;)
                {
                    Edge x = S.top();S.pop();
                    if(bccno[x.u]!=bcc_cnt) {
                        ADD(x.u,id); ADD(id,x.u); be[id]=x.u; bccno[x.u]=bcc_cnt;
                    } if(bccno[x.v]!=bcc_cnt) {
                        ADD(x.v,id); ADD(id,x.v); be[id]=x.v; bccno[x.v]=bcc_cnt;
                    } if(x.u==u&&x.v==v) break;
                }
            }
        }
        else if(pre[v]<pre[u]&&v!=fa) { S.push(e); lowu=min(lowu,pre[v]); }
    }
    return lowu;
}

void find_bcc()
{
    memset(pre,0,sizeof(pre));
    memset(bccno,0,sizeof(bccno));
    dfs_clock=bcc_cnt=0;
    for(int i=1;i<=n;i++) if(!pre[i]) Tarjan(i,-1);
}

void dfs(int x)
{
    sz++; f[x]=1; be[x]=block;
    for(int i=h[x];i!=-1;i=e[i].nx) if(!f[e[i].v]) dfs(e[i].v);
}

void dp(int x)
{
    f[x]=1; pru[x]=w[x]; LL s=0;
    for(int i=head[x];i!=-1;i=tree[i].nx)
    {
        if(f[tree[i].v]) continue;
        dp(tree[i].v); pru[x]=(pru[x]*pru[tree[i].v])%mod;
        s=(s+pru[tree[i].v])%mod;
    }
    s=(s+val[be[x]]*mod_reverse(pru[x])%mod)%mod;
    ans[x]=((SUM-val[be[x]]+mod)%mod+s)%mod;
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) w[i]=(LL)read();

        memset(h,-1,sizeof h); tot=0;
        for(int i=1;i<=m;i++) {
            int u=read(),v=read(); AddEdge(u,v); AddEdge(v,u);
        }

        memset(f,block=sz=0,sizeof f);
        for(int i=1;i<=n;i++)
        {
            if(f[i]) continue;
            block++; sz=0; dfs(i); cnt[block]=sz;
        }

        SUM=0;
        for(int i=1;i<=block;i++) val[i]=1;
        for(int i=1;i<=n;i++) val[be[i]]=(val[be[i]]*w[i])%mod;
        for(int i=1;i<=block;i++) SUM=(SUM+val[i])%mod;

        memset(head,-1,sizeof head); tot=0; id=n;
        find_bcc();

        memset(f,0,sizeof f);
        for(int i=1;i<=n;i++)
            if(cnt[be[i]]==1) ans[i]=(SUM-val[be[i]]+mod)%mod;
        for(int i=n+1;i<=id;i++) if(!f[i]) dp(i);

        LL S=0;
        for(int i=1;i<=n;i++) S=(S+((LL)i*ans[i])%mod)%mod;
        printf("%lld\n",S);
    }
    return 0;
}
时间: 2024-10-03 17:08:26

HDU 5739 Fantasia的相关文章

hdu 5739 割点

Fantasia Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1654    Accepted Submission(s): 429 Problem Description Professor Zhang has an undirected graph G with n vertices and m edges. Each vert

2016多校 #2 1006 Fantasia

http://acm.hdu.edu.cn/showproblem.php?pid=5739 已知一个无向图,每个结点有价值a[i].一个连通图的价值定义为其所有结点价值之积:一个不连通图的价值为其所有连通分量的价值之和. 分别求删除每一个点后图的剩余部分的价值.n<=1e5,m<=2*1e5. 首先,原图可能有若干连通分量,这种情况很好处理.下面只对一个连通分量进行说明. 首先,删除一个点后图可能会分裂成若干部分,所以我们要对割点和非割点分别处理. 首先用tarjan求出所有割点,在此过程中

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

hdu 1207 汉诺塔II (DP+递推)

汉诺塔II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4529    Accepted Submission(s): 2231 Problem Description 经典的汉诺塔问题经常作为一个递归的经典例题存在.可能有人并不知道汉诺塔问题的典故.汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往

[hdu 2102]bfs+注意INF

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2102 感觉这个题非常水,结果一直WA,最后发现居然是0x3f3f3f3f不够大导致的--把INF改成INF+INF就过了. #include<bits/stdc++.h> using namespace std; bool vis[2][15][15]; char s[2][15][15]; const int INF=0x3f3f3f3f; const int fx[]={0,0,1,-1};

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

HDU 5917 Instability ramsey定理

http://acm.hdu.edu.cn/showproblem.php?pid=5917 即世界上任意6个人中,总有3个人相互认识,或互相皆不认识. 所以子集 >= 6的一定是合法的. 然后总的子集数目是2^n,减去不合法的,暴力枚举即可. 选了1个肯定不合法,2个也是,3个的话C(n, 3)枚举判断,C(n, 4), C(n, 5) #include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using name

hdu 6166 Senior Pan

地址:http://acm.split.hdu.edu.cn/showproblem.php?pid=6166 题目: Senior Pan Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 245    Accepted Submission(s): 71 Problem Description Senior Pan fails i