ゲーム理論の演習の[] LGOJ5652の基礎

あなたは、これは道路ゲーム理論の問題であると思います、実際には、それがああ確かです......

言うことを上のアイデアの完全な診察室

最後のフレームの数が奇数の場合まず後方検討し、何が起こりますか?

例をサンプルに

我々は最後のフレームに来れば明らかに、あなただけの、この列のグリッドが完全に消失するまで、繰り返しのクロスの上にジャンプすることができます

最後の1が勝利のポイント(ここでは最初のポイントの勝利のように定義が勝つためにここに来なければならない)である、そのような場合を見つけることは容易です

(ここに来ている場合、次の対戦相手のポイントを獲得することができるであろう来るため)ので、m列の最前列に、列はすべての障害箇所に運命づけられます

ポイント勝利は青が負けの点を表し、赤で表現しました

あなたが青の列(ポイントを失う)、最後の二つが再び交差行が消えるまで、第2行目にジャンプしますを避けるために行きたい場合はその後、検討していき

聞き覚えが?オン!これはケースと同じの始まりです!

しかし、ここで、最後のフレームであっても、明らかにそれは(過去の優位は失われます)、障害ポイントする運命にあるさ

それはそれの何、それの前の行がポイントですか?それが偶数であるため、そう前ケースを継承し、ポイントが失敗する運命にあります

ここでは、我々はすでに上の結論を引き出すことができます。

倒推,如果最后一列是奇数,那么这一列是必胜点,它前面的m列都必败
如果最后一列是偶数,那么这一列是必败点,所有玩家都会尽量避免走到这一列来

だから、私たちも、サイドを考慮することはできません!列挙の順序、各列のため、入れ它前面的第一个必胜点さえ側に彼を

この場合、でも良い地図た後、ゼロ根ざした多分岐ツリーです!

R Lが祖先である場合は、各照会LとRの場合は、その後、上側の手は勝つために、それ以外の場合はFLAC勝利!

だから今の質問は、ツリーの定形を与えられて、どのように\(\シータ(1)\ ) uはそれが祖先であるVかどうかを決定するために?

DFSオーダー!

DFNとして初めてポイントはDFSのコースにアクセスする統計タイムスタンプ、

もし\(DFN [U] \当量 DFN [V] \当量DFN [U] +サイズ[U] -1 \) 、そして、uがVの祖先であります!

そして、この問題は、それを解決します!

具体的にコードを見て、それ

#include<bits/stdc++.h>
#define mod (1LL<<32)
#define ll long long
#define int long long
#define N 20000005
using namespace std;

int n,m,q,type,l[N],r[N];
int L,a[N],fa[N];//fa->这个点前面的第一个必胜转移点 
ll ans=0;

int A,B,C,P;
inline int rnd(){return A=(A*B+C)%P;}

struct Edge
{
    int next,to;
}edge[N];
int cnt=0,head[N];

inline void add_edge(int from,int to)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    head[from]=cnt;
}

template<class T>inline void read(T &res)
{
    char c;T flag=1;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
    while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}

int siz[N],dfn[N],tms;
void dfs(int u,int fa)
{
//  cout<<"dfs: "<<u<<" "<<fa<<endl;
    siz[u]=1;
    if(!dfn[u]) dfn[u]=++tms;
    for(register int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        dfs(v,u);
        siz[u]+=siz[v];
    }
}

signed main()
{
    read(n);read(m);read(q);read(type);//数的个数,区间长度,询问,是否压缩 
    for(register int i=1;i<=n;++i) read(a[i]);
    if(!type)
    {
        for(register int i=1;i<=q;++i)
            read(l[i]),read(r[i]);
    }
    else
    {
        read(A);read(B);read(C);read(P);
        for(register int i=1;i<=q;++i)
        {
            l[i]=rnd()%n+1;
            r[i]=rnd()%n+1;
            if(l[i]>r[i]) swap(l[i],r[i]);
        }
    }
    
    for(register int i=1;i<=n;++i)
    {
        L=max(0LL,i-m-1);
        if(a[i]&1)//奇数是必胜点
        {
            if(a[L]&1) fa[i]=L,add_edge(L,i);
            else fa[i]=fa[L],add_edge(fa[L],i);
        }
        else//偶数,先手必败 
        {
            if(a[i-1]&1) fa[i]=i-1,add_edge(i-1,i);
            else fa[i]=fa[i-1],add_edge(fa[i-1],i);
        }
    }
    //处理出每个点倒序第一个必胜转移点 

    dfs(0,0);//dfs序 
//  for(register int i=1;i<=n;++i) cout<<siz[i]<<endl;
    for(register int i=1;i<=q;++i)
    {
        int u=l[i],v=r[i];
        if(u==v)
        {
            if(a[u]&1) continue;
            else ans=(ans+i*i)%mod;
            continue;
        }
//      cout<<dfn[u]<<" "<<dfn[v]<<" "<<dfn[u]+siz[u]-1<<endl;
        if(dfn[u]<=dfn[v]&&dfn[v]<=dfn[u]+siz[u]-1) continue;//先手必胜,没有贡献 
        ans=(ans+i*i)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

/*
4 2 1 0
2 2 0 0
1 4
*/

おすすめ

転載: www.cnblogs.com/tqr06/p/11854590.html