P1503 鬼子进村
题目链接
题目背景
小卡正在新家的客厅中看电视。电视里正在播放放了千八百次依旧重播的《亮剑》,剧中李云龙带领的独立团在一个县城遇到了一个鬼子小队,于是独立团与鬼子展开游击战。
题目描述
描述 县城里有n个用地道相连的房子,第i个只与第i-1和第i+1个相连。这是有m个消息依次传来
1、消息为D x:鬼子将x号房子摧毁了,地道被堵上。
2、消息为R :村民们将鬼子上一个摧毁的房子修复了。
3、消息为Q x:有一名士兵被围堵在x号房子中。
李云龙收到信息很紧张,他想知道每一个被围堵的士兵能够到达的房子有几个。
输入格式
第一行2个整数n,m(n,m=50000)。
接下来m行,有如题目所说的三种信息共m条。
输出格式
对于每一个被围堵的士兵,输出该士兵能够到达的房子数。
输入样例
7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4
输出样例
1
0
2
4
说明
若士兵被围堵在摧毁了的房子中,那只能等死了。。。。。。
题解
小题是多解的,以至于暴力好像都可以解(洛谷博客中有一位网友声称自己的暴力过了)。
我首先想到的就是分块,记录每个块中最左边和最右边被拆掉的房子(记作ansL和ansR)。
添加的时候,顺便修正这块的ansL和ansR。O(1)
删除的时候,也要修正这块的ansL和ansR。O(k) 其中k为每块的点数。
查询的时候,先看看查询的点所在的块中有没有被拆的,如果没有,那么就看看左边和右边的块里分别有没有 ansR 和 ansL(详见代码)
后来发现还可以用平衡树来做,把拆掉的点加入树中,查询的时候,查找前驱和后继,删除就直接删除。注意,要提前塞入一个 0 和一个 n+1。
代码
分块
#include
#include
#include
#include
#include
#define pd(ch) ch!='R'&&ch!='Q'&&ch!='D'
using namespace std;
const int maxn=5e4+5,maxt=5e2+5;
int n,m,q,tot,id[maxn],L[maxt],R[maxt],ansL[maxt],ansR[maxt],st[maxn];
int vis[maxn];
//id每个点所在组号,L每组左边界,R每组右边界
//vis每个点的状态,ansL和ansR表示每组最左边第一个和最右边第一个拆了的点
void ins(int x)
{
if (vis[x]) return;
vis[x]=1;st[++st[0]]=x;
int y=id[x];
if (ansL[y]==-1||ansL[y]>x) ansL[y]=x;
if (ansR[y]void del(int x)
{
if (!vis[x]) return;
vis[x]=0;int y=id[x],i;
if (ansL[y]==x)
for (i=L[y],ansL[y]=-1;i<=R[y];i++)
if (vis[i]) {ansL[y]=i;break;}
if (ansR[y]==x)
for (i=R[y],ansR[y]=-1;i>=L[y];i--)
if (vis[i]) {ansR[y]=i;break;}
}
int fin(int x)
{
pair<int,int>ans;
ans.first=0,ans.second=n+1;
int y=id[x];
if (ansL[y]!=-1&&ansL[y]<=x)
{
for (int i=x;i>=L[y];i--)
if (vis[i]) {ans.first=i;break;}
}else
{
for (int i=y-1;i>=1;i--)
if (ansR[i]!=-1) {ans.first=ansR[i];break;}
}
if (ansR[y]!=-1&&ansR[y]>=x)
{
for (int i=x;i<=R[y];i++)
if (vis[i]) {ans.second=i;break;}
}else
{
for (int i=y+1;i<=tot;i++)
if (ansL[i]!=-1) {ans.second=ansL[i];break;}
}
return max(0,ans.second-ans.first-1);
}
int main()
{
scanf("%d%d",&n,&q);
m=sqrt(n);
for (int i=1;i<=n;)
{
tot++;ansL[tot]=-1;ansR[tot]=-1;
L[tot]=i;R[tot]=min(i+m-1,n);
for (;i<=R[tot];i++) id[i]=tot;
}
for (int i=1,x;i<=q;i++)
{
char ch=getchar();
while (pd(ch)) ch=getchar();
if (ch!='R') scanf("%d",&x);
if (ch=='R') {if (st[0]>0) del(st[st[0]--]);}else
if (ch=='Q') printf("%d\n",fin(x));else
if (ch=='D') ins(x);
}
return 0;
}
Treap
#include
#include
#include
#include
#include
#define rd() ((rand()|rand()<<15)^(rand()|rand()<<15))
#define pd(ch) (ch!='D'&&ch!='Q'&&ch!='R')
#define INF 100000000
using namespace std;
const int maxn=5e4+5;
int n,m,st[maxn];
bool vis[maxn];
struct js{
int x,r;
js* son[2];
js(){son[0]=son[1]=NULL;r=rd();}
};
js *rot=NULL;
void rotate(js* &o,int p)//用son[p]来代替当前节点
{
js *u=o->son[p];
o->son[p]=u->son[p^1];
u->son[p^1]=o;
o=u;
}
void insert(js* &o,int d)
{
if (o==NULL) {o=new js();o->x=d;}
if (o->x==d) return;
int p=o->xson[p],d);
if (o->son[p]->r>o->r) rotate(o,p);
}
int qia(js* &o,int d)
{
if (o==NULL) return -INF;
return o->x>d?qia(o->son[0],d):max(o->x,qia(o->son[1],d));
}
int hou(js* &o,int d)
{
if (o==NULL) return INF;
return o->xson[1],d):min(o->x,hou(o->son[0],d));
}
void remov(js* &o,int d)
{
int k=(o->son[0]!=NULL)+(o->son[1]!=NULL);
if (k==2)
{
rotate(o,o->son[0]->rson[1]->r);
if (o->son[0]!=NULL&&o->son[0]->x==d) remov(o->son[0],d);
else remov(o->son[1],d);
}else if (k==0) o=NULL;else
{
rotate(o,o->son[1]!=NULL);
if (o->son[0]!=NULL&&o->son[0]->x==d) remov(o->son[0],d);
else remov(o->son[1],d);
}
}
void delet(js* &o,int d)
{
if (o->x==d) {remov(o,d);return;}
int p=o->xson[p],d);
}
int main()
{
srand((int)time(NULL));
scanf("%d%d",&n,&m);
insert(rot,0);insert(rot,n+1);
for (int i=1,x;i<=m;i++)
{
char ch=getchar();
while (pd(ch)) ch=getchar();
if (ch!='R') scanf("%d",&x);
if (ch=='R') {if (st[0]>0&&vis[x=st[st[0]]]) delet(rot,x),vis[x]=0,st[0]--;}else
if (ch=='Q') printf("%d\n",max(0,hou(rot,x)-qia(rot,x)-1));else
{if (!vis[x]) insert(rot,x),vis[x]=1;st[++st[0]]=x;}
}
return 0;
}