题解: $ n<=100 $ 急忙叫队友是不是暴力题啊....
qko:"好像我会O(n)"
我:"好巧,我也会O(n)的,那就莽吧"
四个状态 :
$ dp1[x][0] $表示不摁且这个节点不亮的个数
$ dp1[x][1] $表示不摁且这个节点亮的个数
$ dp2[x][0] $表示摁且这个节点不亮的个数
$ dp2[x][1] $表示摁且这个节点亮的个数
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <stack> #include <queue> #include <cmath> #include <set> #include <map> #define mp make_pair #define pb push_back #define pii pair<int,int> #define link(x) for(edge *j=h[x];j;j=j->next) #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) const int MAXN=3e5+10; const double eps=1e-8; #define ll long long using namespace std; const int inf=1e6; struct edge{int t;edge*next;}e[MAXN<<1],*h[MAXN],*o=e; void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;} ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } int dp1[MAXN][2],dp2[MAXN][2]; bool vis[MAXN]; int n; void dfs(int x,int pre){ int cnt1=0,cnt2=0;int ans1=0,ans2=0; int minn1=inf,minn2=inf; bool flag=0;int cnt=0; link(x){ if(j->t==pre)continue; dfs(j->t,x); flag=1; if(vis[j->t])cnt++; ans1+=min(dp1[j->t][1],dp2[j->t][1]); if(dp2[j->t][1]<=dp1[j->t][1])cnt1++; minn1=min(minn1,max(dp2[j->t][1],dp1[j->t][1])-min(dp2[j->t][1],dp1[j->t][1])); ans2+=min(dp1[j->t][0],dp2[j->t][0]); if(dp2[j->t][0]<=dp1[j->t][0])cnt2++; minn2=min(minn2,max(dp2[j->t][0],dp1[j->t][0])-min(dp2[j->t][0],dp1[j->t][0])); } if(flag){ // cout<<cnt1<<" "<<cnt2<<endl; if(cnt1%2==0)dp1[x][0]=ans1,dp1[x][1]=ans1+minn1; else dp1[x][1]=ans1,dp1[x][0]=ans1+minn1; if(cnt2%2==0)dp2[x][1]=ans2+1,dp2[x][0]=ans2+minn2+1; else dp2[x][0]=ans2+1,dp2[x][1]=ans2+minn2+1; //dp1[x][0]+=cnt;dp1[x][1]+=cnt; } else dp1[x][1]=dp2[x][0]=inf,dp2[x][1]=1,vis[x]=1; //cout<<x<<"::: "<<dp1[x][0]<<" "<<dp1[x][1]<<" "<<dp2[x][0]<<" "<<dp2[x][1]<<endl; } int main(){ while(~scanf("%d",&n)&&n){ memset(h,0,sizeof(h));o=e; memset(dp1,0,sizeof(dp1));memset(dp2,0,sizeof(dp2)); int x,y; inc(i,2,n)x=read(),y=read(),add(x,y),add(y,x); dfs(1,0); printf("%d ",min(dp1[1][1],dp2[1][1])); } return 0; }
2466: [中山市选2009]树
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1413 Solved: 613
[Submit][Status][Discuss]
Description
图论中的树为一个无环的无向图。给定一棵树,每个节点有一盏指示灯和一个按钮。如果节点的按扭被按了,那么该节点的灯会从熄灭变为点亮(当按之前是熄灭的),或者从点亮到熄灭(当按之前是点亮的)。并且该节点的直接邻居也发生同样的变化。
开始的时候,所有的指示灯都是熄灭的。请编程计算最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。
Input
输入文件有多组数据。
输入第一行包含一个整数n,表示树的节点数目。每个节点的编号从1到n。
输入接下来的n – 1行,每一行包含两个整数x,y,表示节点x和y之间有一条无向边。
当输入n为0时,表示输入结束。
Output
对于每组数据,输出最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。每一组数据独占一行。
Sample Input
3
1 2
1 3
0
1 2
1 3
0
Sample Output
1
HINT
对于100%的数据,满足1 <= n <=100。