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

来源:互联网 发布:淘宝店花呗怎么开通 编辑:程序博客网 时间:2024/06/13 09:57

传送门

Description

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

Solution

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

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

Code

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#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");    }}