[BZOJ]2466: [中山市选2009]树

 题解:  $ 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\n",min(dp1[1][1],dp2[1][1]));
    }
    return 0;
}

  

2466: [中山市选2009]树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 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

Sample Output

1

HINT

 

对于100%的数据,满足1 <= n <=100。

posted @ 2019-03-03 17:34  wang9897  阅读(299)  评论(0编辑  收藏  举报