{题解}[jzoj5097]【GDOI2017 day1】取石子游戏

传送门

Description

五千字的满分作文题目……
大概只有两句话
给出一棵以 1 为根带权有根树
求 对于每个点 除它的子树以外结点权值的mex

Solution

画一棵树 我们发现
对于一个点 若其 ans 为 0 则它子树以外不出现 0
同理非零情况

那么 对于所有相同权值的结点的 LCA 到根的路径上 ans 都可能取这个权值
一个显然的做法是 从小到大枚举权值 做 LCA
这样的做法是足够优秀的 LCA 可以用 Tarjan 实现 倍增也够用了

Code

#include 
#include 
#include 
#include 
#include 
#define oo 2139062143
#define sqr(x) ((x)*(x))
#define lowbit(x) ((x)&(-x))
#define abs(x) (((x)>=0)?(x):(-(x)))
#define max(x,y) (((x)>(y))?(x):(y))
#define min(x,y) (((x)<(y))?(x):(y))
#define fo(i,x,y) for (int i = (x);i <= (y);++ i)
#define fd(i,x,y) for (int i = (x);i >= (y);-- i)
#define fm(i,x) for (int i = las[x];i;i = nex[i])
using namespace std;
typedef double db;
typedef long long ll;
const int N = 1001000;
int n,m,cas;
int x,y;
int las[N],last[N],nex[N],a[N],tot;
int f[N][21],dep[N];
int bz[N],az[N],los[N],ans[N],du[N],d[N];
int read()
{
    char ch=getchar();int x=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
void link(int x,int y)
{
    last[++ tot] = y,
    nex[tot] = las[x],
    las[x] = tot;
}
void bfs()
{
    int i,head = 0,tail = 0,x;
    fo(i,1,n)
        if (!du[i])
            d[++ tail] = i;
    dep[1] = 1;
    while (head < tail)
    {
        x = d[++ head];
        fm(i,x)
        {
            -- du[last[i]];
            if (!du[last[i]]) d[++ tail] = last[i];
            f[last[i]][0] = x;
            dep[last[i]] = dep[x] + 1;
        }
    }
}
int lca(int x,int y)
{
    if (dep[x] < dep[y]) swap(x,y);
    fd(i,20,0)
        if (dep[f[x][i]] > dep[y]) x = f[x][i];
    if (dep[x] != dep[y]) x = f[x][0];
    fd(i,20,0) 
        if(f[x][i] != f[y][i]) x = f[x][i],y = f[y][i];
    if (x != y) return f[x][0];
    else return x;
}
int main()
{
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    for (scanf("%d", &cas);cas;cas --)
    {
        tot=0;
        memset(las,0,sizeof(las)); memset(bz,0,sizeof(bz));
        memset(ans,255,sizeof(ans)); memset(du,0,sizeof(du));
        scanf("%d%d", &n, &m);
        fo(i,1,n) a[i] = read();
        fd(i,n,1) los[i] = bz[a[i]],bz[a[i]] = i;
        fo(i,1,n - 1) 
        {
            int x = read(),y = read();
            link(x,y);
            ++ du[y];
        }
        bfs();
        fo(j,1,20)
            fo(i,1,n)
                f[i][j] = f[f[i][j - 1]][j - 1];
        fo(i,0,m)
        {
            x = bz[i],y = 0;

            if (!x)
            {
                fo(j,1,n)
                    if (ans[j] == -1)
                        ans[j] = i;
                break;
            }
            while (x)
            {
                if (!y) y = x; else y = lca(y,x);
                x = los[x];
            }

            while (y && ans[y] == -1) ans[y] = i,y = f[y][0];
        }
        fo(i,1,n)
        {
                if (ans[i] == -1) printf("0 ");
                else printf("%d ",ans[i]);
        }
        printf("\n");
    }
}

你可能感兴趣的:({题解}[jzoj5097]【GDOI2017 day1】取石子游戏)