打地鼠 - 乱搞

题目大意:有n只地鼠,在1到n,进行m轮操作,每次形如将x位置上的老鼠打死,然后每一只老鼠必须分别向左或者向右跳一步(跳出边界就死了)。
对每只地鼠求,其是否一定会挂,如果不是,最终能到达多少位置。
n , m 10 6
题解:送命题系列。
显然每轮结束后每个地鼠能到达的位置是一个区间,这样可以平方dp。
首先如果已经确定没有地鼠会死掉,那么左右端点是独立的,例如右端点每轮除了边界和被打的位置都会+1,这两个位置会-1。
考虑有老鼠挂了怎么做。发现这些区间左端点随右端点递增而不降,因此可以用一个并查集或者线段树什么的维护一波。
考虑一个线性做法,维护LF(x)表示所有区间左端点在x的地鼠中,标号最小的一个。显然这玩意是单调的(准确的说如果不算死掉的地鼠的话)。然后发现每一轮转移是数组整体移动和一点小小的变化,用一个指针维护即可。那个判是否会死有点奇怪,自行理解一下。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 1000010
#define gc getchar()
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int *lf=new int[N<<1],*rf=new int[N<<1]+N;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
char tt[20],ss[N*10];int ttl,ssl;
int L[N],R[N],cnt[N],s[10];
inline int show(int x)
{
    if(x==0) ss[++ssl]='0';
    for(ttl=0;x;x/=10) tt[++ttl]=x%10+'0';
    for(;ttl;ttl--) ss[++ssl]=tt[ttl];
    return ss[++ssl]=' ';
}
int main()
{
    int n=inn(),m=inn();rep(i,1,n) lf[i]=rf[i]=i,lf[n+i]=n+1;lf[0]=1,rf[n+1]=n;
    rep(i,1,m)
    {
        int x=inn();
        if(lf[x]<=rf[x]) cnt[lf[x]]++,cnt[rf[x]+2]--;//?
        lf++,rf--,lf[x+1]=lf[x-1],rf[x-1]=rf[x+1],lf[2]=lf[0],rf[n-1]=rf[n+1];
//      debug(i)ln;rep(j,1,n) debug(j)sp,debug(lf[j])sp,debug(rf[j])ln;cerr ln;
    }
//  rep(i,1,n) debug(i)sp,debug(lf[i])sp,debug(rf[i])ln;
    rep(i,1,n) for(int j=lf[i];j<lf[i+2];j+=2) L[j]=i;
    rep(i,1,n) for(int j=rf[i];j>rf[i-2];j-=2) R[j]=i;
    rep(i,1,n) show((s[i&1]+=cnt[i])?0:(R[i]-L[i])/2+1);
    return ss[++ssl]='\n',fwrite(ss+1,sizeof(char),ssl,stdout),0;
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/82462798