1002:City Upgrading
题意:
给定一颗树,每个点有一个价格,现在要在点上部署路由器使得所有点都能收到信号,点上部署路由器需要支付当前点上的价格,每个路由器可以让当前点和相邻节点收到信号,问让全部节点都收到信号的最小花费。
考点:树形dp
思路:当最开始看到这个题的时候最先想到了285. 没有上司的舞会 - AcWing题库
这一题,就是树形dp的板子题,然后按照这一题的代码改了一下就出来了第一版,但是因为仿照没有上司的舞会用了两个状态:安装路由器和不安装路由器导致有情况没有被包含所以wa了。
未考虑的情况就是:安-不安-不安-安,这种情况没法只用两种状态表示。
错误代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include <map>
#include <vector>
#include <cmath>
#define endl "\n"
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<int,PII> PIII;
#define int LL
const int N=200010,mod=998244353;
int mon[N];
int n;
int h[N],e[N],ne[N],idx;
bool has_fa[N];
int dp[N][2];
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u){
dp[u][1]=mon[u];
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
dfs(j);
dp[u][1]+=min(dp[j][0],dp[j][1]);
dp[u][0]+=dp[j][1];
}
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>mon[i];
}
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b);
has_fa[b]=1;
has_fa[a]=1;
}
int rool=1;
has_fa[rool]=0;
dfs(rool);
cout<<min(dp[rool][0],dp[rool][1])<<endl;
}
signed main(){
quick_cin();
int t;
cin>>t;
while(t--){
idx=0;
memset(h,-1,sizeof h);
memset(dp,0,sizeof dp);
solve();
}
return 0;
}
比赛时倒是想到增加状态了,但是一直没想对,比赛后看了标程才知道,其实只要将原来的不安装分成两种状态即可:1、不安装但是可以被其他路由器包含,2、不安装但是不能被其他路由器包含。
然后参照标程给的转移方程终于做对了QAQ:
#include<iostream>
#include<cstring>
#include<algorithm>
#include <map>
#include <vector>
#include <cmath>
#define endl "\n"
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<int,PII> PIII;
#define int LL
const int N=200010,mod=998244353,INF=0x3f3f3f3f3f3f3f3f;
int mon[N];
int n;
int h[N],e[N],ne[N],idx;
int dp[N][3];//0:不安装但是不能被其他路由器包含 1:安装 2:不安装但是可以被其他路由器包含,012状态的基础都是子树已经合法的被全覆盖
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u,int fa){
dp[u][0]=0,dp[u][2]=INF;//状态2初始要设置成inf,要是设为0会违背状态的定义,也就是子树并没有被全覆盖
dp[u][1]=mon[u];
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
if(j==fa) continue;
dfs(j,u);
dp[u][2]=min(dp[u][2]+min(dp[j][1],dp[j][2]),dp[j][1]+dp[u][0]);
dp[u][1]+=min(dp[j][0],min(dp[j][1],dp[j][2]));
dp[u][0]+=min(dp[j][1],dp[j][2]);
}
}
void solve(){
memset(h,-1,sizeof h);
idx=0;
memset(dp,0,sizeof dp);
cin>>n;
for(int i=1;i<=n;i++){
cin>>mon[i];
}
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
dfs(1,0);
cout<<min(dp[1][1],dp[1][2])<<endl;
}
signed main(){
quick_cin();
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
1012:Play on Tree
题意:俩人在一颗树上玩删点游戏,每次删除会把选中的点以及其子树删除,并且树的根节点在玩游戏前是未知的,问先手胜出的概率的逆元。
考点:SG函数、博弈论
wa原因:比赛时想到了是博弈论、也想到了nim游戏,但是后来思路偏了,一直在找奇偶点的关系,没想到sg函数。
正解思路:
首先我们可以看出,在树根固定的情况下,只有必胜或者必败两种状态。然后从叶子节点开始考虑,首先,叶子节点是先手必败,所以其sg函数的值为0,然后再考虑其父节点,因为存在叶子节点,所以其父节点的sg函数值应该为所有子节点sg函数值+1的异或(所用到的公式:sg(x+y)=sg(x)^sg(y)两个子树合成一个大子树、sg(x)=mex{sg(y)|x->y}求sg(x)的值)通过这样的思路先将以点1为根的sg值以及各个子树的sg值算出来,然后再通过异或的性质进行换根操作(详情看代码),即可得出所有点的sg值
#include<iostream>
#include<cstring>
#include<algorithm>
#include <map>
#include <vector>
#include <cmath>
#define endl "\n"
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<int,PII> PIII;
#define int LL
const int N=200010,mod=1e9+7,INF=0x3f3f3f3f3f3f3f3f;
int sg[N],sg_all[N];
int n;
int h[N],e[N*2],ne[N*2],idx;
LL qmi(int a, int b, int p)
{
LL res = 1;
while(b){
if(b & 1) res = res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u,int fa){
int sg_now=0;
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
if(j==fa) continue;
dfs(j,u);
sg_now^=(sg[j]+1);
}
sg[u]=sg_now;
}
void dfs2(int u,int fa){
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
if(j==fa) continue;
sg_all[j]=sg[j]^((sg_all[u]^(sg[j]+1))+1);//换根
dfs2(j,u);
}
}
void solve(){
memset(h,-1,sizeof h);
idx=0;
cin>>n;
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
dfs(1,1);
sg_all[1]=sg[1];
dfs2(1,1);
int ans=0;
for(int i=1;i<=n;i++){
if(sg_all[i]) ans++;
}
cout<<(ans*qmi(n,mod-2,mod))%mod<<endl;
}
signed main(){
quick_cin();
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}