HDU 5739 Fantasia 2016多校

来源:互联网 发布:mac适合的浏览器 编辑:程序博客网 时间:2024/05/29 09:08

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5739
转载声明:http://blog.csdn.net/cqu_hyx/article/details/51995638

题意:
给你一个图G,每个顶点都有权值,然后删除某个点.计算该图的权值.求权值有2步
1.如果这个图是连通的,权值就是所有点的乘积.
2.如果这个图不是连通的,把分散的图按1来算,然后∑Gi;
输出答案:
这里写图片描述

个人感想:
首先我们得有预备知识,Tarjan算法求割点.
我做这题我也不会,我是磨了2天这个算法我才回顾这道题的,当然我也参考了转载的大牛的题解.
我不太懂正解的方法,感觉好复杂..这是也是超级大牛境界啊..好了回归正题吧.

首先我们一样的会想到很朴素的算法,暴力,但是暴力是(n^2)必然超时.所以我们得用其他的方法.我们其实想想就挺容易找到突破口(虽然不会做..),如果我们删除的当前的这个点不是割顶,那么我们就可以把整个原图的整个权值Sum求了,然后除以当前不是割顶的权值,就可以得到删除该点的权值就不用重新求一遍.如果当前的顶点是割顶.那么如果我们快速的知道分散的几块图的权值,那就好办了..(可是我也没什么头绪,对模板不熟).

参考了一下参考转载文章..瞬间对模板的理解有了大幅度的提升.首先我们得理解一下搜索树的概念,这不详细说.
首先预处理整个图的权值Sum.注意原图可能是分散了几块(所以每一块我们也得预处理出当前图的val).
假设图val[i]代表以i为根的图的权值.
首先TarJan算法就是建立在搜索树之上,在遍历好搜索树的时候,通过Tarjan算法,我们很容易知道当前这个点是不是割顶
这里写图片描述
这里假设 Sum=val[1]+val[6];
假设w[i]代表i点的权值
假设 t【i】代表以i节点为根的树权值
这里要分几种情况讨论:
1.不是割顶
例如第4个点.那么我们就好求了因为除去这个点对原图并没有太大的影响. 所以
去了第4个点后的权值为:G=Sum-val[1]+val[1]/w[4] ;
2.是割顶
例如第3个点.显然去了第3个点的时候,图就分成了4块,那怎么办.
这里写图片描述
这是搜索树,我们明显可以发现把如果我们知道了3点的子树的所有权值加上,3点上部分的权值不就好了..求子树(不存在回边到3点祖先点的子树,看过tarjan都会明白的)的权值应该很容易 回溯一下就好了,然后保存在一个tmp中,假设这是 tmp=t[4]+t[8];可是上部分又怎么求?我们不是求了Sum了吗?我们明显知道了啊.
所以去了3点的图的权值就为G=Sum-val【1】+val【1】/t[3]+tmp

《———到这里好像完事了,其实还没呢..我要提2个注意点才行..所以我才说是分几种————–》

3.独立点(参考文章的,没子节点)
例如点6,这个点又没子节点那么我们怎么求去掉这个点的权值呢,这我画出来就是让大家注意的地方呢..
因为我们知道他是根节点,而且又没子节点..那就好求咯.直接看公式。 G=Sum-val【2】;
4.根节点(参考文章的)
因为我们知道Tarjan算法对根节点是进行特殊处理的.他的重点就不言而喻.
根节点分也是分两种情况! 不是割顶. 是顶.只要我们同样的像上面两种情况一样做操作一样不会有问题.
不过根节点在求割顶的时候有个快速的计算公式
G=Sum-val[1]+tmp 并不需要除数.

通过这几步我们通过ans[i] 存在去了i点之后Gi的权值. 然后求出 Ans=∑i*ans【i】%mod 就是答案了..
我是通过手模参考文章才发现这些问题了.对TarJan算法了解更深了..莫莫莫,感觉以后这些问题也不成问题了..

分析:Tarjan求割顶算法
代码:

/* Author:GavinjouElephant * Title: * Number: * main meanning: * * * *///#define OUT#include <iostream>using namespace std;#include <cstdio>#include <cmath>#include <cstring>#include <algorithm>#include <sstream>#include <cctype>#include <vector>#include <set>#include <cstdlib>#include <map>#include <queue>//#include<initializer_list>//#include <windows.h>//#include <fstream>//#include <conio.h>#define MaxN 0x7fffffff#define MinN -0x7fffffff#define Clear(x) memset(x,0,sizeof(x))typedef long long ll;const int INF=0x3f3f3f3f;const ll maxn=2e5+10;const ll mod=1e9+7;ll T;ll N,M;struct  Edge{    ll to;    ll next;};Edge edge[5*maxn];ll head[maxn];ll tot,Index;ll LOW[maxn];ll DFN[maxn];ll ans[maxn];ll Sum;bool cut[maxn];//判断是否为割顶;ll w[maxn];//获取每个点的权值ll val[maxn];//算出子树的所有乘积,其实有用的只是根..ll top;//s根起点的计数ll s[maxn];//根起点bool vis[maxn];//dfs标记,ll root;void addedge(ll u,ll v){     edge[tot].to=v;     edge[tot].next=head[u];     head[u]=tot++;}ll qpow(ll a,ll n){    ll res=1;    while(n)    {        if(n&1)res=(res*a)%mod;        a=(a*a)%mod;        n>>=1;    }    return res;}ll inv(ll x)//费马小定理{    return qpow(x,mod-2);}ll Tarjan(ll u,ll p)//kuangbin模板Tarjan{    ll res=w[u];    ll sum=0;    ll pro=w[u];    ll v;    LOW[u]=DFN[u]=++Index;    ll son=0;    ll pre_cnt=0;    bool flag=true;//是否为孤立点.    for(int j=head[u];j!=-1;j=edge[j].next)    {        v=edge[j].to;        if(v == fa && pre_cnt ==0){pre_cnt++;continue;}        if(!DFN[v])        {            son++;            ll tmp=Tarjan(v,u);            LOW[u]=min(LOW[u],LOW[v]);            if(u==fa)  sum=(sum+tmp)%mod;            if(u==fa &&son>1)flag=false;            if(u!=fa &&LOW[v]>=DFN[u])            {                cut[u]=true;                flag=false;                sum=(sum+tmp)%mod;                pro=(pro*tmp)%mod;            }            res=(res*tmp)%mod;        }        else if(LOW[u]>DFN[v])            LOW[u]=DFN[v];    }    if(flag)    {         if(u==fa && head[u]==-1) ans[u]=(Sum-val[root]+mod)%mod;//如果这个是孤立点,而且是父节点,直接用整个图的总权值相减就好了.         else ans[u]=( (Sum-val[root]+mod)%mod +(val[root]*inv(w[u])%mod))%mod;//否则这不是割顶,那么直接去掉这个.要分块!!    }    else    {         ll pre=val[root]*inv(pro)%mod;         if(u!=fa ) sum=(sum+pre)%mod;         ans[u]=(Sum-val[root]+mod)%mod;         ans[u]=(ans[u]+sum)%mod;    }    return res;}void init(){    memset(head,-1,sizeof(head));    memset(DFN,0,sizeof(DFN));    Sum=top=tot=Index=0;    memset(cut,false,sizeof(cut));    memset(vis,false,sizeof(vis));    memset(ans,0,sizeof(ans));}void dfs(ll u){    vis[u]=true;    val[u]=w[u];    for(int j=head[u];j!=-1;j=edge[j].next)    {        ll v=edge[j].to;        if(!vis[v])        {           dfs(v);           val[u]=(val[u]*val[v])%mod;        }    }}int main(){#ifdef OUT    freopen("coco.txt","r",stdin);    freopen("lala.txt","w",stdout);#endif    scanf("%I64d",&T);    while(T--)    {        init();        ll x,y;        scanf("%I64d%I64d",&N,&M);        for(int i=1;i<=N;i++)        {            scanf("%I64d",&w[i]);        }        for(int i=0;i<M;i++)        {          scanf("%I64d%I64d",&x,&y);          addedge(x,y);          addedge(y,x);        }        for(int i=1;i<=N;i++)        {            if(vis[i])continue;            dfs(i);            s[top++]=i;            Sum=(Sum+val[i])%mod;        }        for(int i=0;i<top;i++)        {            root=s[i];            Tarjan(s[i],s[i]);        }        ll Ans=0;        for(int i=1;i<=N;i++)        {            Ans=(Ans+(i*ans[i])%mod)%mod;        }        printf("%I64d\n",Ans%mod);    }    return 0;}

朴素想法代码:

/* Author:GavinjouElephant * Title: * Number: * main meanning: * * * *///#define OUT#include <iostream>using namespace std;#include <cstdio>#include <cmath>#include <cstring>#include <algorithm>#include <sstream>#include <cctype>#include <vector>#include <set>#include <cstdlib>#include <map>#include <queue>//#include<initializer_list>//#include <windows.h>//#include <fstream>//#include <conio.h>#define MaxN 0x7fffffff#define MinN -0x7fffffff#define Clear(x) memset(x,0,sizeof(x))typedef long long ll;const int INF=0x3f3f3f3f;int T;int N;const int maxn=1e5;const int mod=1e9+7;string str;int f[maxn];int w[maxn];ll dp[maxn];int opt(int a,int b){    if(str[0]=='A') return a&b;    if(str[0]=='O') return a|b;    return a^b;}void init(){    memset(f,-1,sizeof(f));    memset(dp,0,sizeof(dp));}ll V(int s){   int tmp=f[s];   while(tmp!=-1)   {       dp[s]=max(dp[s],dp[tmp]+opt(w[s],w[tmp]));       tmp=f[tmp];       //cout<<tmp<<endl;   }   return dp[s];}int main(){#ifdef OUT    freopen("coco.txt","r",stdin);    freopen("lala.txt","w",stdout);#endif   scanf("%d",&T);   while(T--)   {     init();     scanf("%d",&N);     cin>>str;     ll ans=0;     for(int i=1;i<=N;i++) scanf("%d",&w[i]);     for(int i=2;i<=N;i++)     {         scanf("%d",&f[i]);     }     for(int i=1;i<=N;i++)     {           ans+=(i*(w[i]+V(i)))%mod;     }     printf("%I64d\n",ans);   }    return 0;}
0 0
原创粉丝点击