[2017纪中11-3]机房比教室好多了 博弈+树型DP

来源:互联网 发布:java算法书籍推荐 编辑:程序博客网 时间:2024/06/05 20:21

题面
先考虑出发点在x,先手玩以x为根这棵子树的答案。
首先不难证明,a[x]变大,不会变得更劣。
那我们考虑先手拿完一个石头后走向一个a[son]>=a[x]的儿子,这样肯定是不明智的,因为假如对手把你推回x,你当然不能和他一直这样推来推去(你肯定先死),所以你会选择去别的儿子,但这当然不如一开始就去别的儿子(因为a[x]更大)。
再考虑先手拿完一个石头后走向一个a[son]

#include<iostream>#include<cstdio>#include<cstring>#define ll long longusing namespace std;const int maxn=1000010;int n,a[maxn],s[maxn];bool f[maxn];struct edge{    int t;    edge *next;}*con[maxn];int read(){    int x=0;    char ch=getchar();    while(ch<'0'||ch>'9') ch=getchar();    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}    return x;}void ins(int x,int y){    edge *p=new edge;    p->t=y;    p->next=con[x];    con[x]=p;}void dfs(int v,int fa){    for(edge *p=con[v];p;p=p->next)        if(p->t!=fa)         {            dfs(p->t,v);            if(a[p->t]<a[v]&&s[p->t]==0) s[v]++;        }}void dfs2(int v,int fa){    if(a[fa]<a[v]) s[v]+=(f[v]==0);        for(edge *p=con[v];p;p=p->next)            if(p->t!=fa)             {                f[p->t]=(s[v]-(a[v]>a[p->t]&&s[p->t]>0));                       dfs2(p->t,v);            }}int main(){    n=read();    for(int i=1;i<=n;i++)        a[i]=read();        for(int i=1;i<n;i++)    {        int x=read(),y=read();        ins(x,y);        ins(y,x);    }    dfs(1,0);    for(int i=1;i<=n;i++)        if(s[i]>0) printf("%d ",i);    return 0;}

考场怕爆栈dfs序版:

#include<iostream>#include<cstdio>#include<cstring>#define ll long longusing namespace std;const int maxn=1000010;int n,a[maxn],s[maxn],fa[maxn],st[maxn],dfn[maxn],tim;bool f[maxn];struct edge{    int t;    edge *next;}*con[maxn];int read(){    int x=0;    char ch=getchar();    while(ch<'0'||ch>'9') ch=getchar();    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}    return x;}void ins(int x,int y){    edge *p=new edge;    p->t=y;    p->next=con[x];    con[x]=p;}void dfs(){    int top=1;st[1]=1;fa[1]=0;    while(top>0)    {        int v=st[top--];        dfn[++tim]=v;        for(edge *p=con[v];p;p=p->next)            if(p->t!=fa[v])             {                fa[p->t]=v;                st[++top]=p->t;            }    }    for(int i=n;i>1;i--){int v=dfn[i];if(a[v]<a[fa[v]]&&s[v]==0) s[fa[v]]++;}    for(int i=2;i<=n;i++)    {        int v=dfn[i];        f[v]=(s[fa[v]]-(a[fa[v]]>a[v]&&s[v]>0));        if(a[fa[v]]<a[v]) s[v]+=(f[v]==0);    }}int main(){    n=read();    for(int i=1;i<=n;i++)        a[i]=read();        for(int i=1;i<n;i++)    {        int x=read(),y=read();        ins(x,y);        ins(y,x);    }    dfs();      for(int i=1;i<=n;i++)        if(s[i]>0) printf("%d ",i);    return 0;}

其实并不需要这么麻烦。考虑一条边(x,y),x,y之间只会转移一次(因为a[x]

#include<cstdio>#include<algorithm>using namespace std;const int N=1e6+10;inline int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}    return x*f;}struct node{    int to,nxt;}tree[N<<2];int num[N],head[N],cnt=0,vis[N],n,can[N];void add(int x,int y){    tree[++cnt]=node{y,head[x]};    head[x]=cnt;}int dfs(int u,int fa){    if (vis[u]) return can[u];    vis[u]=1;can[u]=0;    for (int i=head[u];i;i=tree[i].nxt){        int v=tree[i].to;        if (v==fa) continue;        if (num[u]>num[v]&&!dfs(v,fa)){            can[u]=1;            break;        }    }    return can[u];}int main(){    n=read();    for (register int i=1;i<=n;++i) num[i]=read();    for (register int i=1,x,y;i<n;++i)        x=read(),y=read(),        add(x,y),add(y,x);    for (register int i=1;i<=n;i++)        if (dfs(i,0))   printf("%d ",i);} 
原创粉丝点击