GDKOI2024普及组

儿童节快乐捏

捉迷藏

Zayin和Ziyin在一棵\(n\)个节点的树上,Zayin从\(a\)节点开始,每次可以走\(da\)步,Ziyin从\(b\)节点开始,每次可以走\(db\)步,走到了另一个人所在的节点的人获胜。求在最优策略下,两者谁会获胜。

题解:

\(a\)\(b\)之间的距离是\(dis\)

简单易得

\(dis<da\)时,Zayin胜

否则,当\(da>db\)时,Zayin胜

否则,当\(da=db\)时,平局

否则,当\(da<db\)时,Ziyin胜

其中\(dis\)使用倍增求\(lca\)求解

代码

#include<cstdio>
#include<cstring>
const int N=1000005;
int n,head[N],pedge,dep[N],fa[N][22],t,q;
struct Edge{
    int to,next;
}edge[N<<1];
inline void Ins(int u,int v){edge[++pedge]={v,head[u]},head[u]=pedge;}
inline void ins(int u,int v){Ins(u,v),Ins(v,u);}
inline void swap(int&x,int&y){x^=y^=x^=y;}
void prework(int u,int father){
    fa[u][0]=father,dep[u]=dep[father]+1;
    for(int i=1;1<<i<=dep[u];i++)fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int e=head[u];e;e=edge[e].next){
        int v=edge[e].to;
        if(v^father)prework(v,u);
    }
}
inline int lca(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    for(int i=20;~i;i--)if(dep[fa[u][i]]>=dep[v])u=fa[u][i];
    if(u==v)return u;
    for(int i=20;~i;i--)if(fa[u][i]^fa[v][i])u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
int main(){
    for(scanf("%*d%d",&t);t--;){
        scanf("%d%d",&n,&q),memset(head+1,0,n<<2);
        for(int i=1;i<=n;i++)memset(fa[i],0,84);
        for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),ins(u,v);
        prework(1,0);
        for(int a,b,da,db,dis;q--;){
            scanf("%d%d%d%d",&a,&b,&da,&db),dis=dep[a]+dep[b]-dep[lca(a,b)]*2;
            if(dis<=da||da>db)puts("Zayin");
            else if(da<db)puts("Ziyin");
            else puts("Draw");
        }
    }
    return 0;
}

读书

题面:

每次读书的第\(i\)个章节,都需要至少已经阅读其他的\(a_i\)个章节,每天从头到尾读书,把能读的章节读了,最少需要几天,读不完输出\(-1\)

题解:

每天内读书相当与找一个最小值\(min\),若\(min\le0\)那么可以读书并将其后面的\(a_i\)全部减\(1\),将这个\(a_i\)赋值成\(\infty\),在没有\(min\le0\)后,把每个设置成\(\infty\)的数前面也减去\(1\)并进入下一天。

如果\(n\)天结束还没有读完书,那么就读不完了。

使用线段树维护区间加最小值。

代码

#include<cstdio>
const int N=500005,INF=0x3f3f3f3f;
int n,a[N],tot,stk[N],top;
struct Tree{
    int l,r,minn,pos,tag;
}tr[N<<2];
inline int min(int x,int y){return x<y?x:y;}
inline int ls(int u){return u<<1;}
inline int rs(int u){return u<<1|1;}
inline void pushup(int u){
    if(tr[ls(u)].minn>tr[rs(u)].minn)tr[u].minn=tr[rs(u)].minn,tr[u].pos=tr[rs(u)].pos;
    else tr[u].minn=tr[ls(u)].minn,tr[u].pos=tr[ls(u)].pos;
}
inline void push(int u,int x){
    tr[u].minn+=x,tr[u].tag+=x;
}
inline void pushdown(int u){
    if(tr[u].tag)push(ls(u),tr[u].tag),push(rs(u),tr[u].tag),tr[u].tag=0;
}
void build(int u,int l,int r){
    tr[u].l=l,tr[u].r=r;
    if(l==r)return tr[u].minn=a[l],tr[u].pos=l,void();
    int mid=(l+r)>>1;
    build(ls(u),l,mid),build(rs(u),mid+1,r),pushup(u);
}
void modify(int u,int l,int r,int x){
    if(l<=tr[u].l&&r>=tr[u].r)return push(u,x);
    int mid=(tr[u].l+tr[u].r)>>1;
    pushdown(u);
    if(l<=mid)modify(ls(u),l,r,x);
    if(r>mid)modify(rs(u),l,r,x);
    pushup(u);
}
int main(){
    scanf("%*d%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",a+i);
    build(1,1,n);
    for(int i=1;i<=n+1;modify(1,1,n,-top),top=0,i++){
        if(i==n+1){
            puts("-1");
            break;
        }
        for(;tr[1].minn<=0;){
            int p=tr[1].pos;
            if(p^n)modify(1,p+1,n,-1);
            modify(1,p,p,INF),stk[++top]=p,tot++;
            if(tot==n)break;
        }
        if(tot==n){
            printf("%d\n",i);
            break;
        }
        for(int i=1;i<=top;i++)if(stk[i]^n)modify(1,stk[i]+1,n,1);
    }
    return 0;
}

正方形拓展

每个点会按正方形拓展,两个图形相遇后不再拓展,有\(n\)个起始点,问哪些点可以无限拓展,哪些点不能。

可以无限拓展的点只需要在上下左右任何一个方向可以无限拓展,不能无限拓展的点在四个方向都不能无限拓展。

考虑每个点和一个方向如向上

当这个点不能沿着这个方向拓展时,有两种情况

第一种情况是另一个点在这个点的正上方,如图(蓝色点为讨论的点,红色点为阻拦的点,黑色为分界线)

[pk8x8u8.md.png]

第二种情况下,先讨论两个不在一条竖直直线上时,形成的图形,如图
[pk8x1jf.md.png]

在有两个点阻拦的情况下,有如下两种情况(绿色点为另一个阻拦的点)

[pk8xlgP.md.png]

[pk8xGDS.md.png]

所以在第二种情况中,右边与左上方有点,左边与右上方有点,左上方与右上方有点的三种方法都可以阻拦向上无限拓展。

所以对每个点分别维护它的上,下,左,右,左上,左下,右上,右下的八个方向是否分别有点,并结合八个方向上是否有点,得出该点是否可以无限拓展。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
const int N=1000005;
int n,tx[N],nx,ty[N],ny,tr[N];
bool u[N],d[N],l[N],r[N],lu[N],ld[N],ru[N],rd[N],res[N],ans[N];
struct Point{
    int x,y,id;
    Point(){}
    Point(int _x,int _y){x=_x,y=_y;}
}a[N];
inline bool cmp(const Point&a,const Point&b){return a.x^b.x?a.x<b.x:a.y<b.y;}
inline void add(int p,int x){for(;p<=ny;p+=p&-p)tr[p]+=x;}
inline int que(int p){
    int res=0;
    for(;p;p-=p&-p)res+=tr[p];
    return res;
}
inline int sum(int l,int r){return que(r)-que(l-1);}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",tx+i,ty+i),a[i]=Point(tx[i],ty[i]);
    std::sort(tx+1,tx+n+1),nx=std::unique(tx+1,tx+n+1)-tx-1,std::sort(ty+1,ty+n+1),ny=std::unique(ty+1,ty+n+1)-ty-1;
    for(int i=1;i<=n;i++)a[i]=Point(std::lower_bound(tx+1,tx+nx+1,a[i].x)-tx,std::lower_bound(ty+1,ty+ny+1,a[i].y)-ty),a[i].id=i;
    std::sort(a+1,a+n+1,cmp);
    for(int i=1,j;i<=n;i=j+1){
        for(j=i;a[j+1].x==a[i].x;j++);
        for(int k=i;k<=j;k++){
            if(sum(1,a[k].y-1))ld[k]=true;
            if(sum(a[k].y+1,ny))lu[k]=true;
            if(sum(a[k].y,a[k].y))l[k]=true;
        }
        for(int k=i;k<=j;k++)add(a[k].y,1);
    }
    memset(tr,0,sizeof(tr));
    for(int i=n,j;i;i=j-1){
        for(j=i;a[j-1].x==a[i].x;j--);
        for(int k=j;k<=i;k++){
            if(sum(1,a[k].y-1))rd[k]=true;
            if(sum(a[k].y+1,ny))ru[k]=true;
            if(sum(a[k].y,a[k].y))r[k]=true;
        }
        for(int k=j;k<=i;k++)add(a[k].y,1);
    }
    memset(tr,0,sizeof(tr));
    for(int i=1,j;i<=n;i=j+1){
        for(j=i;a[j+1].x==a[i].x;j++);
        for(int k=i;k<=j;k++)add(a[k].y,1);
        for(int k=i;k<=j;k++){
            if(sum(1,a[k].y-1))d[k]=true;
            if(sum(a[k].y+1,ny))u[k]=true;
        }
        for(int k=i;k<=j;k++)add(a[k].y,-1);
    }
    for(int i=1;i<=n;i++){
        if(lu[i]&&ru[i]||lu[i]&&r[i]||ru[i]&&l[i])u[i]=true;
        if(lu[i]&&ld[i]||lu[i]&&d[i]||ld[i]&&u[i])l[i]=true;
        if(ru[i]&&rd[i]||ru[i]&&d[i]||rd[i]&&u[i])r[i]=true;
        if(ld[i]&&rd[i]||ld[i]&&r[i]||rd[i]&&l[i])d[i]=true;
        if(l[i]&&u[i]&&r[i]&&d[i])res[i]=true;
    }
    for(int i=1;i<=n;i++)ans[a[i].id]=res[i];
    for(int i=1;i<=n;i++)putchar(ans[i]?'0':'1');
    return puts(""),0;
}

最后,还是儿童节快乐捏。

posted @ 2024-06-01 15:43  junjunccc  阅读(29)  评论(1编辑  收藏  举报