zoukankan      html  css  js  c++  java
  • [ZJOI]骑士

    一开始在loj上看到,不知道难度,一看,这不是没有上司的舞会吗......

    然后样例没过,哦,这不是棵树,那么怎样成为一棵树?

    删一条边,枚举复杂度会超,代码也没调出来,就看了题解......

    作者: HullEssien 更新时间: 2017-09-25 19:56  在Ta的博客查看  14 


    本题解比较啰嗦

    尽力用最容易明白的语言来讲

    希望对大家有帮助

    基环树

    一个听起来很高大上的名词

    但是不要被这个名字吓跑了

    其实这个题

    思路很简单

    没比一道普通的树形DP难多少

    (依然不懂这个题为什么省选/NOI-,明明和树形DP入门题没有上司的舞会那么像)

    只是细节比较多而已

    (其实也不算很多啦)

    认真读完题目我们会发现

    因为一个骑士有且只有一个最讨厌的人

    而且这个骑士不会讨厌自己

    即该图中是没有自环的

    然后 从网上搜的很多题解都是用无向图存边

    然后用一些高超的技巧(如位运算)判断卡掉无向二元环

    但是题解中的kczno1

    这位神犇就没有使用无向图

    事实证明存无向图是完全没有必要的

    因为本身有向图就携带着指向上一个节点的信息

    而且这个信息更利于维护我们删边之后的操作

    这样 不仅省去了判断的麻烦

    还有利于维护信息

    何乐而不为?

    所以 考虑这个有向图

    我们把x所讨厌的人y设为x的父亲节点

    这样考虑每一个人都有且只有一条出边

    所以对一个"联通块"

    只有根节点有机会形成环

    即环一定包含根节点

    为什么呢?

    因为一个点的出度只能为1

    考虑非根节点

    它的出度一定是贡献给它的父亲的

    而根节点它的出度只能贡献给它的后代

    (这里的"根节点""叶子节点"都只是为了描述方便,并不严谨,也许可以理解为"删边以后的叶子和根"?)

    所以我们又解决了一个问题:

    每个联通块内有且只有一个简单环

    这样 我们考虑把每个联通块的环上删一条边

    这样它必然构成树

    然后要注意

    删掉的边所连接的两点x,y

    是不能同时选的

    所以我们分别强制x,y其中一个点不选

    对新树跑DP

    显然相邻的点是不能选的

    所以得到状态转移方程:

    用f[i][0/1]表示以i为根节点的子树选i/不选i所能获得的最大价值

    则 f[i][0]=sigema(max(f[son][0],f[son][1])); for each son of i

    f[i][1]=sigema(f[son][0]); for each son of i

    应该就很清楚了

    再一个细节就是

    答案会爆int

    我交了数遍

    都卡在这里

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 using namespace std;
     5 typedef long long ll;
     6 const int maxn=1e6+7;
     7 int val[maxn],head[maxn],n,fa[maxn],ss,num;
     8 ll ans,f[maxn][3];
     9 bool vis[maxn];
    10 struct Edge{
    11     int next;int to;
    12 }edge[maxn];
    13 int read(){
    14     int f=1;int x=0;char s=getchar();
    15     while(s<'0'||s>'9'){if(s=='-') f=-1;s=getchar();}
    16     while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    17     x*=f;
    18     return x;
    19 }
    20 void add(int from,int to){
    21     edge[++num].next=head[from];
    22     edge[num].to=to;
    23     head[from]=num;
    24 }
    25 int dfs(int x){
    26     vis[x]=true;
    27     f[x][0]=0;f[x][1]=val[x];
    28     for(int i=head[x];i;i=edge[i].next){
    29         int v=edge[i].to;
    30         if(v!=ss){
    31             dfs(v);
    32             f[x][1]+=f[v][0];
    33             f[x][0]+=max(f[v][0],f[v][1]);
    34         }     
    35         else{
    36             f[v][1]=-2147483647;
    37         }    
    38     }
    39 }
    40 void find(int x){
    41     vis[x]=true;
    42     while(vis[fa[x]]==false){
    43         x=fa[x];
    44         vis[x]=true;
    45     }
    46     ss=x;
    47     dfs(ss);
    48     ll an=max(f[ss][0],f[ss][1]);
    49     vis[ss]=true;
    50     ss=fa[ss];
    51     dfs(ss);
    52     ans+=max(an,max(f[ss][0],f[ss][1]));
    53 }
    54 int main(){
    55     n=read();
    56     for(int i=1;i<=n;i++){
    57         val[i]=read();fa[i]=read();
    58         add(fa[i],i);
    59     }
    60     for(int i=1;i<=n;i++){
    61         if(!vis[i]) find(i);
    62     }
    63     cout<<ans<<endl;
    64     return 0; 
    65 }

    还有就是树形DP写的太复杂了

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 typedef long long ll;
     6 const int maxn=1e6+7;
     7 int fa[maxn],num,n,head[maxn],val[maxn];
     8 ll f[maxn][7],ans;
     9 bool vis[maxn];
    10 struct Edge{
    11     int next;int to;
    12 }edge[maxn];
    13 void add(int from,int to){
    14     edge[++num].next=head[from];
    15     edge[num].to=to;
    16     head[from]=num;
    17 }
    18 int dfs(int x,int opt){
    19     if(head[x]==0){
    20         if(opt==0||vis[x]==true) return 0;
    21         else return val[x];
    22     }
    23     ll ans1=0;
    24     if(opt==1){
    25         for(int i=head[x];i;i=edge[i].next){
    26             int v=edge[i].to;
    27             ans1+=dfs(v,0);
    28         }
    29         ans1+=val[x];
    30     }
    31     if(opt==0){
    32         for(int i=head[x];i;i=edge[i].next){
    33             int v=edge[i].to;
    34             ans1+=max(dfs(v,0),dfs(v,1));
    35         }
    36     }
    37     return ans1;
    38 }
    39 int main(){
    40     cin>>n;
    41     for(int i=1;i<=n;i++){
    42         cin>>val[i]>>fa[i];
    43         add(fa[i],i);
    44     }
    45     for(int i=1;i<=n;i++){
    46         vis[i]=true;
    47         int aa=head[i];head[i]=0;
    48         ll t=dfs(fa[i],1);
    49         vis[i]=false;
    50         t+=dfs(fa[i],0);
    51         ans=max(ans,t);
    52         head[i]=aa;
    53     }
    54     cout<<ans<<endl;
    55     return 0; 
    56 }
  • 相关阅读:
    SEO工作中如何增加用户体验?10个细节要注意!
    网站优化不等于搜索引擎优化SEO
    新站不收录内页的原因及解决办法
    网站搜索引擎优化(SEO)的18条守则
    网站优化(SEO)的10大误区
    网页设计师必知的10则SEO
    站内SEO规范
    影响SEO 搜索引擎优化的网页制作细节
    推荐两个国外网站-帮你优化网站SEO和预测下期的PR值
    简单介绍百度与谷歌关于收录的不同之处
  • 原文地址:https://www.cnblogs.com/lcan/p/9574911.html
Copyright © 2011-2022 走看看