題解:CF1926G

一只小咕咕發表於2024-05-03

題目傳送門

思路

發現權值為 C 的點可以選擇看做是權值為 S 或為 P 的點,所以問題轉換為怎麼給 C 點賦值可以使答案最小,考慮樹形 dp。

\(f_{i,0/i,1}\) 表示 \(i\) 點賦值為 SP 時最少要刪除幾條邊。但如果當前點權值不為 C 的話,那顯然他的父親節點應該選擇和他權值相同的點才最優,所以可以把權值相反時的 \(f_i\) 賦值為一個很大的數,這樣就不會被選擇了。

程式碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int inf=0x3f3f3f3f;
inline int read();
int T,n,a[N],cnt,head[N],f[N][2];
bool vis[N]; 
struct E{
	int to,next;
}edge[N<<1];
void add(int u,int v)
{
	edge[++cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt;
}
void dfs(int x)
{
	vis[x]=1;
	for(int i=head[x];i;i=edge[i].next)
	{
		int to=edge[i].to;
		if(vis[to]) continue;
		dfs(to);
		f[x][0]+=min(f[to][0],f[to][1]+1);
		f[x][1]+=min(f[to][1],f[to][0]+1);
	}
	if(a[x]==1) f[x][0]=inf;
	else if(a[x]==-1) f[x][1]=inf;
}
int main()
{
	T=read();
	for(int d=1;d<=T;d++)
	{
		cnt=0;
		memset(head,0,sizeof head);
		memset(a,0,sizeof a);
		memset(vis,0,sizeof vis);
		for(int i=1;i<=n;i++) f[i][0]=f[i][1]=0;
		n=read();
		for(int i=2;i<=n;i++)
		{
			int x;
			x=read();
			add(i,x);
			add(x,i);
		}
		for(int i=1;i<=n;i++)
		{
			char ch;
			cin>>ch;
			if(ch=='S') a[i]=1;
			else if(ch=='P') a[i]=-1;
		}
		dfs(1);
		printf("%d\n",min(f[1][0],f[1][1]));
	}
	return 0;
}

inline int read()
{
	int x=0,f=1;
	char ch;
	ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')
	{
		x=(x<<1)+(x<<3)+(ch&15);
		ch=getchar();
	}
    return x*f;
}