Day4 樹的直徑、重心以及基環樹

~hsm~發表於2019-02-13

A. POJ 1985 Cow Marathon 樹的直徑-模板

題目

POJ 1985

程式碼

樹形DP求樹的直徑

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=4e4+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],edge[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y,int z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
int d[maxn<<1],v[maxn<<1],ans;
inline void dp(int x)
{
	v[x]=1;
	for (int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if (v[y]) continue;
		dp(y);
		ans=max(ans,d[x]+d[y]+edge[i]);
		d[x]=max(d[x],d[y]+edge[i]);
	}
}
int main()
{
	int n,m;read(n);read(m);
	for (int i=1;i<=m;++i)
	{
		int x,y,z;char s;
		read(x);read(y);read(z);
		cin>>s;
		add(x,y,z),add(y,x,z);
	}
	dp(1);
	printf("%d\n",ans);
	return 0;
}

BFS 求樹的直徑

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=4e4+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],edge[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y,int z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
int d[maxn<<1],v[maxn<<1],ans,n,m,point;
inline void bfs(int s)
{
	memset(v,0,sizeof(v));
	memset(d,0,sizeof(d));
	queue<int> q;
	v[s]=1;
	q.push(s);
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		for (int i=head[x];i;i=Next[i])
		{
			int y=ver[i];
			if (!v[y])
			{
				v[y]=1;
				d[y]=d[x]+edge[i];
				q.push(y);
			}
		}
	}
	ans=0;
	for (int i=1;i<=n;++i)
		if (d[i]>ans)
		{
			ans=d[i];
			point=i;
		}
}
int main()
{
	read(n);read(m);
	for (int i=1;i<=m;++i)
	{
		int x,y,z;char s;
		read(x);read(y);read(z);
		cin>>s;
		add(x,y,z),add(y,x,z);
	}
	bfs(1),bfs(point);
	printf("%d\n",ans);
	return 0;
}

B. POJ 1849 Two 樹的直徑

題目

POJ 1849

程式碼

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=4e4+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],edge[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y,int z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
int d[maxn<<1],v[maxn<<1],ans;
inline void dp(int x)
{
	v[x]=1;
	for (int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if (v[y]) continue;
		dp(y);
		ans=max(ans,d[x]+d[y]+edge[i]);
		d[x]=max(d[x],d[y]+edge[i]);
	}
}
int main()
{
	int n,m,sum=0;read(n);read(m);
	for (int i=1;i<n;++i)
	{
		int x,y,z;
		read(x);read(y);read(z);
		add(x,y,z),add(y,x,z);
		sum+=z;
	}
	dp(1);
	printf("%d\n",(sum<<1)-ans);
	return 0;
}

C. POJ 1655 Balancing Act 樹的重心-模板

題目

POJ 1655

程式碼

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=2e4+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') f=-1, ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int vis[maxn],siz[maxn],ans,pos,n;
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
	ver[++len]=y,Next[len]=head[x],head[x]=len;
}
inline void dfs(int x)
{
    vis[x]=1,siz[x]=1;
    int maxpart=0;
    for (int i=head[x];i;i=Next[i])
    {
    	int y=ver[i];
    	if (!vis[y])
		{
            dfs(y);
            siz[x]+=siz[y];
            maxpart=max(maxpart,siz[y]);
        }
	}
    maxpart=max(maxpart,n-siz[x]);
    if (maxpart<ans || (maxpart==ans && x<pos))
		ans=maxpart,pos=x;
}
int main()
{
    int t;read(t);
    while (t--)
	{
        read(n);
        memset(vis,0,sizeof(vis));
		memset(head,0,sizeof(head));
        len=pos=0;
		ans=0x3f3f3f3f;
        for (int x,y,i=1;i<n;++i)
		{
            read(x);read(y);
            add(x,y);add(y,x);
		}
        dfs(1);
        printf("%d %d\n",pos,ans);
    }
    return 0;
}

來自Tyouchie的鄰接矩陣寫法

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=2e4+10;
const int INF=0x3f3f3f3f;
vector<int>G[maxn];
int son[maxn];
int ans,n,balance,t;

void dfs(int v,int fa)
{
	son[v]=1;
	int d=G[v].size();
	int pre_balance=0;
	for (int i=0;i<d;i++)
	{
		int k=G[v][i];
		if (k!=fa)
		{
			dfs(k,v);
			son[v]+=son[k];
			pre_balance=max(pre_balance,son[k]);
		}
	}
	pre_balance=max(pre_balance,n-son[v]);
	if (pre_balance<balance || (pre_balance==balance&&v<ans))
	{
		ans=v;
		balance=pre_balance;
	}
}
int main()
{
	cin>>t;
	while (t--)
	{
		scanf("%d",&n);
		for (int i=1;i<=n;++i)
			G[i].clear();
		for (int i=1;i<n;i++)
		{
			int s,e;
			scanf("%d%d",&s,&e);
			G[s].push_back(e);
			G[e].push_back(s);
		}
		memset(son,0,sizeof(son));
		ans=0;balance=INF;
		dfs(1,0);
		cout<<ans<<' '<<balance<<endl;
	}
	return 0;
}

NOIP 2018 旅行

題目

LUOGU 5022

題解

1.如果m==n1m==n-1的話,這就是一棵普通的樹,我們只要貪心選點(編號較小的優先遍歷)進行dfs即可。
2.如果m==nm==n的話,就是樹多連了一條邊,即基環樹,那麼求一個基環樹的字典序最小的遍歷序列該怎麼辦,經過老師的講解,以及看了半天題解後,我們可以想到我們會求樹的遍歷序列,而基環樹就是樹多加一條邊,那麼我們就可以列舉刪邊,然後依照第一種情況來寫,複雜度是O(n2)。
最後要說明的是:這份程式碼會T最後三個點。未來改進。

程式碼

#include<bits/stdc++.h>
using namespace std;
const int maxn=5050;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
vector<int>G[maxn];
int ans[maxn],edge[maxn][2];
int n,m;
namespace solve1
{
	int cnt=0;
	bool vis[maxn];
	void dfs(int x,int fa)
	{
		ans[++cnt]=x;
		vis[x]=1;
		for (int i=0;i<G[x].size();++i)
		{
			int y=G[x][i];
			if (!vis[y])
				dfs(y,x);
		}
	}
	void main()
	{
		memset(vis,0,sizeof(vis));
		memset(ans,0,sizeof(ans));
		cnt=0;//注意初始化
		for (int i=1;i<=n;++i)
			sort(G[i].begin(),G[i].end());//貪心選點,先遍歷編號小的點
		dfs(1,0);
		for (int i=1;i<=n;++i)
			printf("%d ",ans[i]);
	}
}
namespace solve2
{
	int cnt=0,res[maxn];
	bool vis[maxn];
	bool cmp()
	{
		for (int i=1;i<=n;++i)
			if (ans[i]!=res[i])
				return ans[i]>res[i];//得到的答案不是未連通圖的情況
		return false;
	}
	int deletex,deletey;
	bool check(int x,int y)//避免這條邊已經被刪掉,然而又去遍歷的情況
	{
		if ((x==deletex&&y==deletey) || (x==deletey&&y==deletex))
			return false;
		return true;
	}
	void dfs(int x,int fa)
	{
		vis[x]=1;
		res[++cnt]=x;
		for (int i=0;i<G[x].size();++i)
		{
			int y=G[x][i];
			if (!vis[y] && check(x,y))
				dfs(y,x);
		}
	}
	void main()
	{
		memset(ans,0x3f,sizeof(ans));
		memset(vis,0,sizeof(vis));
		for (int i=1;i<=n;++i)
            sort(G[i].begin(),G[i].end());//同上
		for (int i=1;i<=m;++i)
		{
			cnt=0;
			memset(res,0,sizeof(res));
			memset(vis,0,sizeof(vis));//初始化
			deletex=edge[i][0];
			deletey=edge[i][1];//列舉刪邊
			dfs(1,0);
			if (cmp()&&cnt==n)//已經統計出了答案且答案不是未連通圖的情況
				memcpy(ans,res,sizeof(res));
		}
		for (int i=1;i<=n;++i)
			printf("%d ",ans[i]);
	}
}
int main()
{
	read(n);read(m);
	for (int i=1;i<=m;++i)
	{
		int x,y;
		read(x);read(y);
		G[x].push_back(y);
        G[y].push_back(x);
		edge[i][0]=x;//將要假刪除的一條邊
        edge[i][1]=y;
	}
	if (m==n-1)
		solve1::main();//如果是一棵樹的話
	else
		solve2::main();//如果是基環樹的話
	return 0;
}

D. BZOJ 1791: [IOI2008]Island 島嶼 基環樹直徑

題目

BZOJ 1791
LUOGU 4381

程式碼

/**************************************************************
    Problem: 1791
    User: sjh
    Language: C++
    Result: Accepted
    Time:8908 ms
    Memory:95044 kb
****************************************************************/ 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1,ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}
int n,m,t;//t是識別符號
int ver[maxn<<1],edge[maxn<<1],next[maxn<<1],head[maxn],tot,du[maxn];
void add(int x,int y,int z)
{
    ver[++tot]=y,edge[tot]=z,next[tot]=head[x],head[x]=tot,++du[y];
}
int c[maxn];//環上的點
int v[maxn];
int q[maxn<<1];
void bfs(int s,int t)
{
    int l,r;
    q[l=r=1]=s;//手寫佇列維護
    c[s]=t;//標記連通塊(看每個節點屬於哪個基環樹)
    while (l<=r)
    {
        for (int i=head[q[l]]; i; i=next[i])
            if (!c[ver[i]])
            {
                q[++r]=ver[i];
                c[ver[i]]=t;
            }
        l++;
    }
}
ll f[maxn];//每顆子樹的直徑
ll d[maxn];//每個節點的子樹大小
void topsort()//找環操作順便處理一種情況(直徑不經過環)
{
    int l=1,r=0,x,y;
    for (int i=1; i<=n; ++i)
        if (du[i]==1)//無向圖度數為1
            q[++r]=i;
    while (l<=r)
    {
        for (int i=head[x=q[l]]; i; i=next[i])
            if (du[y=ver[i]]>1)//度大於1可更新答案
            {
                d[c[x]]=max(d[c[x]],f[x]+f[y]+edge[i]);//子樹內最長鏈
                f[y]=max(f[y],f[x]+edge[i]);//f[x]表示x子樹中離x最遠的點的距離
                if ((--du[y])==1)
                    q[++r]=y;
            }
        l++;
    }
}
ll a[maxn<<1];
ll b[maxn<<1];
void dp(int t,int x)
{
    int m=0,i,l=0,r,y=x;
    do
    {
        a[++m]=f[y];
        du[y]=1;
        for (i=head[y]; i; i=next[i])
            if (du[ver[i]]>1)//點在環上
            {
                y=ver[i];
                b[m+1]=b[m]+edge[i];
                break;
            }
    } while (i);//此時答案為 f[i]+f[j]+dis[i][j]的最大值,dis[i][j]表示環上i到j的最遠距離
    if (m==2)//跑到環外,需要特判
    {
        for (i=head[y]; i; i=next[i])
            if (ver[i]==x)
                l=max(l,edge[i]);
        d[t]=max(d[t],f[x]+f[y]+l);
        return;
    }
    for (i=head[y]; i; i=next[i])//連線環的首尾
        if (ver[i]==x)
        {
            b[m+1]=b[m]+edge[i];
            break;
        }
    for (i=1; i<m; ++i)//由於是環,所以複製一份
    {
        a[m+i]=a[i];
        b[m+i]=b[m+1]+b[i];
    }
    q[l=r=1]=1;
    for (i=2; i<2*m; ++i)
    {
        while (l<=r && i-q[l]>=m) ++l;
        d[t]=max(d[t],a[i]+a[q[l]]+b[i]-b[q[l]]);
        while (l<=r && a[q[r]]+b[i]-b[q[r]]<=a[i]) --r;//單調佇列維護
        q[++r]=i;
    }
}
int main()
{
    read(n);
    for(int i=1; i<=n; ++i)
    {
        int x,y;
        read(x);read(y);
        add(i,x,y),add(x,i,y);
    }
    for (int i=1; i<=n; ++i)
        if (!c[i])
            bfs(i,++t);//統計有多少基環樹
    topsort();//拓撲找環
 
    memset(v,0,sizeof(v));//重新利用v陣列,當作基環樹是否算過
    ll ans=0ll;
    for (int i=1; i<=n; ++i)
        if (du[i]>1 && !v[c[i]])//每個基環樹只跑一遍並且此時i是環上一點
        {
            v[c[i]]=1;
            dp(c[i],i);//求基環樹的直徑
            ans+=d[c[i]];
        }
    printf("%lld\n",ans);
    return 0;
}

E. CF 835F Roads in the Kingdom 基環樹-刪邊

題目

LUOGU CF 835F
CF 835F

解釋

這道題我是真的不會,看了兩堂課,最後只能以抄程式碼了結。。。附上大佬的連結:Rayment_cc

程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const ll inf=1e18;
template<typename T>inline int getmin(T &x,T y)
{
	return x>y?x=y,1:0;
}
template<typename T>inline int getmax(T &x,T y)
{
	return x<y?x=y,1:0;
}
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],edge[maxn<<1],Next[maxn<<1],head[maxn],len=-1;
inline void add(int x,int y,int z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
int cnt;
inline int pre(int x)
{
	return x==1?cnt:x-1;
}
inline int nxt(int x)
{
	return x==cnt?1:x+1;
}
bool vis[maxn];
int cir[maxn];
ll sum[maxn];
inline int dfs(int x,int fa)
{
	if (vis[x]) return x;
	int tmp;
	vis[x]=1;
	for (int i=head[x];~i;i=Next[i])
		if (i^fa)
			if (tmp=dfs(ver[i],i^1))
			{
				if (tmp==-1) return -1;
				cir[++cnt]=x;
				sum[cnt]=edge[i];
				return tmp==x?-1:tmp;
			}
	return 0;
}
ll dist[maxn],mxdist;
int id;
inline void bfs(int s,int ban1,int ban2)
{
	queue<pair<int,int> >q;
	mxdist=dist[s]=0ll;
	id=0;
	q.push(make_pair(s,-1));
	while (!q.empty())
	{
		int x=q.front().first,u=q.front().second;
		q.pop();
		if (getmax(mxdist,dist[x])) id=x;
		for (int i=head[x];~i;i=Next[i])
		{
			int y=ver[i],z=edge[i];
			if (i^u && y^ban1 && y^ban2)
			{
				dist[y]=dist[x]+z;
				q.push(make_pair(y,i^1));
			}
		}
	}
}
ll dep[maxn],mx,ans,tmp;
ll p1[maxn],p2[maxn];
ll s1[maxn],s2[maxn];
ll m1[maxn],m2[maxn];
int main()
{
	int n;
	read(n);
	memset(head,0xff,sizeof(head));
	for (register int i=1;i<=n;++i)
	{
		int x,y,z;
		read(x);read(y);read(z);
		add(x,y,z);add(y,x,z);
	}

	dfs(1,-1);
	for (register int i=1;i<=cnt;++i)
	{
		sum[i]+=sum[i-1];
		bfs(cir[i],cir[pre(i)],cir[nxt(i)]);
		dep[i]=mxdist;
		bfs(id,cir[pre(i)],cir[nxt(i)]);
		getmax(mx,mxdist);
	}

	ans=inf;
	p1[0]=p2[0]=s1[cnt+1]=s2[cnt+1]=s2[cnt+2]=-inf;
	for (register int i=1;i<=cnt;++i)
	{
		m1[i]=max(m1[i-1],dep[i]+sum[i]+p1[i-1]);
		p1[i]=max(p1[i-1],dep[i]-sum[i]);
		p2[i]=max(p2[i-1],dep[i]+sum[i]);
	}
	
	for (register int i=cnt;i;--i)
	{
		m2[i]=max(m2[i+1],dep[i+1]-sum[i+1]+s2[i+2]);
		s1[i]=max(s1[i+1],dep[i]-sum[i]);
		s2[i]=max(s2[i+1],dep[i]+sum[i]);
	}
	
	for (register int i=1;i<cnt;++i)
	{
		tmp=0ll;
		getmax(tmp,m1[i]);
		getmax(tmp,m2[i]);
		getmax(tmp,sum[cnt]+s1[i+1]+p2[i]);
		getmin(ans,tmp);
	}

	tmp=0ll;
	for (register int i=2;i<=cnt;++i)
		getmax(tmp,dep[i]+sum[i]+p1[i-1]);
	getmin(ans,tmp);
	getmax(ans,mx);
	printf("%lld\n",ans);
	return 0;
}

F. BZOJ 1040: [ZJOI2008]騎士 基環樹DP

題目

BZOJ 1040
LUOGU 2607

程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5e2;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
	ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int n,power[maxn],hate[maxn],vis[maxn],U,V;
ll f[maxn],g[maxn];
inline void dfs(int x,int fa)//dfs找環
{
	vis[x]=1;
	for (int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if (y!=fa)
		{
			if (!vis[y]) dfs(y,x);
			else
			{
				vis[y]=1;
				U=x,V=y;
				return ;
			}
		}
	}
}
inline void tree_dp(int x,int fa,int rt,int ban)//ban 不選的點
{
	vis[x]=1;
	f[x]=power[x];
	g[x]=0;
	for (int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if (x==rt && i==ban) continue;
		if (y!=fa && y!=rt)
		{
			tree_dp(y,x,rt,ban);
			f[x]+=g[y];
			g[x]+=max(g[y],f[y]);
		}
	}
}
int main()
{
	read(n);
	for (int i=1;i<=n;++i)
	{
		read(power[i]);read(hate[i]);
		add(i,hate[i]),add(hate[i],i);
	}
	ll ans=0;
	for (int i=1;i<=n;++i)
		if (!vis[i])
		{
			dfs(i,-1);
			int banu,banv;
			for (int i=head[U];i;i=Next[i])
				if (ver[i]==V)
				{
					banu=i;
					break;
				}
			for (int i=head[V];i;i=Next[i])
				if (ver[i]==U)
				{
					banv=i;
					break;
				}
			tree_dp(U,-1,U,banu);//斷環為鏈並將斷開的兩個點強制其中一個點為根且不選,做一次樹形DP
			ll uans=g[U];
			tree_dp(V,-1,V,banv);//對另一個點做同樣操作
			ll vans=g[V];
			ans+=max(uans,vans);//取兩次結果最大值加入ans
		}
	printf("%lld\n",ans);
	return 0;
}

小結

今天的題目真是和江蘇數學卷一樣,前半部分送分,後半部分送命。各個省選難度以上,所以,後三道題寫了程式碼的,其實都是抄題解的,不過也稍微理解了點,集訓結束後,還要回來把這些題再好好寫寫。。。。話說從明天開始,基本上題題都是省選級別以上,什麼Tanjan之類的,雖然之前看過,也寫了一點,然而還是很懵,我後面該怎麼過啊!

相關文章