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

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

#include
#include
#include
#define ll long long
using 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]s[p->t]==0) s[v]++;
        }
}
void dfs2(int v,int fa)
{
    if(a[fa]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;iint 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
#include
#include
#define ll long long
using 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]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]]s[v]+=(f[v]==0);
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();    
    for(int i=1;iint 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
#include
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;ifor (register int i=1;i<=n;i++)
        if (dfs(i,0))   printf("%d ",i);
} 

你可能感兴趣的:(dp,树,博弈)