AtCoder Regular Contest 097F: Monochrome Cat 題解

這道題的思路非常巧妙
首先我們可以把那些全部都是黑點的子樹扔掉,他們是不會被遍歷的
這時,剩下的樹的所有的葉子都是白色的
我們先考慮終點和起點重合的情況
這時如果我們想把它全部變黑的話必須遍歷整棵樹
並且我們發現,無論以哪個點爲起點,答案都是一樣的,首先所有的邊都要走兩遍,第二,一個點被走過的次數等於它的度數,所以一個點是否需要停留一秒也是可以算出來的(顯然的是,一個點最多停留一秒),這樣我們可以先預處理一個need數組,need[i]=1表示在i這個節點需要停留1s
這時我們得出了一個初始答案
我們再考慮終點離開起點的情況,來優化答案
我們發現當終點和起點不在一起時,從起點到終點的路徑上所有的邊只會走一次,還有這條路徑上除了終點的其他點訪問的次數都會-1,也就是說,如果原來這個節點需要停留1s,現在就不需要停留了,可以節省一秒,否則就要額外停留,時間增加1s
於是我們可以獲得這樣一個問題:樹上的每個點都有一個數1或0(這個數就是need數組)我們要在樹上找一條路徑,最大化“點數+1的個數-0的個數”
我們找的這條路徑是不包含終點的路徑,所以點數就是邊數,終點的訪問次數不-1的情況也被考慮了,但需要額外注意的是,因爲我最後需要將終點加到這條鏈的某一端,所以我找的這條鏈的兩端不能同時是葉子
我們考慮樹型dp
dp[i][0]表示以i爲根的子樹中,從某一個葉子出發到i的鏈,上述答案最大的是多少
dp[i][1]表示以i爲根的子樹中,不從葉子出發到i的鏈,上述答案最大是多少
dp2[i]表示以i爲根的子樹中,合法的所有鏈中,上述答案最大的是多少
轉移非常簡單,注意一下dp2[i]不能由“dp[son1][0]+dp[son2][0]+根的貢獻”得到就好
還有幾個細節
1. 我在搜索的時候,一定要以一個白點爲根搜索,否則我的根那裏可能是要被預先砍掉的
2. 理論上,只包含一個單獨的葉子的路徑是合法的,但要特判一下整棵樹只有一個白點的情況


更新:感謝Curious_Cat_is_OIer的評論,事實上是不用考慮葉子的影響的,因爲我所有的葉子都是白色的,而且葉子的相鄰節點一定只有一個,所以它的need一定是0,所以選中一個葉子一定會使點數+1,然後多花1的代價,所以如果一條最大權值鏈裏面包含葉子,把葉子去掉也是最大權值鏈,所以直接找最大權值鏈就可以了,不用像上面一樣考慮奇怪的情況

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pii pair<double,double>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=1e9;
const int magic=348;
const double eps=1e-10;
const double pi=acos(-1);

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

int n;
vector<int> v[100048];int col[100048],d[100048],need[100048];
//0: leaf reachable; 1: leaf unreachable
int dp[100048][2],dp2[100048];
char s[100048];
int haswhite[100048];
int ans,maxminus=0;
int cnt=0;

inline void dfs(int cur,int father)
{
    haswhite[cur]=col[cur];
    int i,y;
    for (i=0;i<int(v[cur].size());i++)
    {
        y=v[cur][i];
        if (y!=father)
        {
            dfs(y,cur);
            if (haswhite[y]) haswhite[cur]=1;
        }
    }
}

inline void solve(int cur,int father)
{
    int i,y;bool isleaf=true;
    for (i=0;i<int(v[cur].size());i++)
    {
        y=v[cur][i];
        if (y!=father && haswhite[y])
        {
            isleaf=false;
            solve(y,cur);
        }
    }
    if (isleaf)
    {
        dp[cur][0]=1+(need[cur]==1?1:-1);
        dp[cur][1]=-INF;
        dp2[cur]=dp[cur][0];
        if (cnt>1) maxminus=max(maxminus,dp2[cur]);
        return;
    }
    dp[cur][0]=-INF;dp[cur][1]=dp2[cur]=1+(need[cur]==1?1:-1);
    int nmax=-INF,ymax=-INF;
    for (i=0;i<int(v[cur].size());i++)
    {
        y=v[cur][i];
        if (y!=father && haswhite[y])
        {
            dp[cur][0]=max(dp[cur][0],dp[y][0]+1+(need[cur]==1?1:-1));
            dp[cur][1]=max(dp[cur][1],dp[y][1]+1+(need[cur]==1?1:-1));
            dp2[cur]=max(dp2[cur],nmax+max(dp[y][0],dp[y][1])+1+(need[cur]==1?1:-1));
            dp2[cur]=max(dp2[cur],ymax+dp[y][1]+1+(need[cur]==1?1:-1));
            nmax=max(nmax,dp[y][1]);ymax=max(ymax,dp[y][0]);
        }
    }
    maxminus=max(maxminus,dp2[cur]);
    maxminus=max(maxminus,max(dp[cur][0],dp[cur][1]));
}

int main ()
{
    int i,j,x,y;
    n=getint();
    for (i=1;i<=n-1;i++)
    {
        x=getint();y=getint();
        v[x].pb(y);v[y].pb(x);
    }
    scanf("%s",s+1);
    int root=-1;
    for (i=1;i<=n;i++) {col[i]=(s[i]=='W');if (col[i] && root==-1) root=i;}
    if (root==-1)
    {
        printf("0\n");
        return 0;
    }
    dfs(root,-1);ans=0;
    for (i=1;i<=n;i++)
        if (haswhite[i])
            for (j=0;j<int(v[i].size());j++)
                if (haswhite[v[i][j]]) ans++,d[i]++;
    for (i=1;i<=n;i++) if (haswhite[i]) cnt++,need[i]=(d[i]%2)^col[i],ans+=need[i];
    solve(root,-1);
    printf("%d\n",ans-maxminus);
    return 0;
}
發佈了182 篇原創文章 · 獲贊 40 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章