学长测试题原题。
题解:神奇的线段树优化建图+拓扑排序。
对于一个地雷来说,我们可以在将所有地雷按照坐标排序后二分得到所能引爆的地雷区间,然后从可以被其引爆的地雷向其连单向边,这个可以用线段树优化建图来搞;为了统计答案,我们还要维护每个点所能控制的区间端点的位置。最后用拓扑排序更新每个点所能引爆的最大区间,统计答案即可。
ps:RE的是由于爆栈了……学长的数据还是太弱
CODE:
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=110005;
struct node
{
ll x,r;
int id;
inline bool operator <(const node &other)const{return x<other.x;}
}bomb[N];
struct edge
{
int nxt,to;
}a[N<<4],e[N<<4];
int s[N<<2],top;
bool b[N<<2];
int head[N<<2],Head[N<<2];
int dfn[N<<2],low[N<<2],belong[N<<2],in[N<<2],pos[N<<2],left[N<<2],right[N<<2],l[N<<2],r[N<<2],ans[N<<2];
ll tmp[N<<2];
int T,n,m,num,Num,Time,block,tot;
queue<int> q;
inline int max(const int &a,const int &b){return a>b?a:b;}
inline int min(const int &a,const int &b){return a<b?a:b;}
inline void add(int x,int y)
{
a[++num].nxt=head[x],a[num].to=y,head[x]=num;
}
inline void Add(int x,int y)
{
e[++Num].nxt=Head[x],e[Num].to=y,Head[x]=Num;
}
void build(int l,int r,int now)
{
left[now]=l,right[now]=r;
if(now!=1) add(now,now>>1);
if(l==r){pos[l]=now;tot=max(tot,now);return;}
int mid=(l+r)>>1;
build(l,mid,now<<1);
build(mid+1,r,now<<1|1);
}
void insert(int L,int R,int l,int r,int now,int num)
{
if(L<=l&&r<=R){if(num!=now)add(now,num);return;}
int mid=(l+r)>>1;
if(L<=mid) insert(L,R,l,mid,now<<1,num);
if(R>mid) insert(L,R,mid+1,r,now<<1|1,num);
}
void dfs(int now)
{
dfn[now]=low[now]=++Time;
s[++top]=now;b[now]=1;
for(int i=head[now];i;i=a[i].nxt)
if(!dfn[a[i].to])
{
dfs(a[i].to);
low[now]=min(low[now],low[a[i].to]);
}
else if(b[a[i].to]) low[now]=min(low[now],dfn[a[i].to]);
if(dfn[now]==low[now])
{
int tmp;
l[++block]=1e9;
r[block]=0;
do tmp=s[top--],b[tmp]=0,belong[tmp]=block,l[block]=min(l[block],left[tmp]),r[block]=max(r[block],right[tmp]);
while(tmp!=now);
}
}
inline void topologysort()
{
for(int i=1;i<=block;i++)
if(!in[i]) q.push(i);
while(!q.empty())
{
int tmp=q.front();q.pop();
for(int i=Head[tmp];i;i=e[i].nxt)
{
in[e[i].to]--;
l[e[i].to]=min(l[e[i].to],l[tmp]);
r[e[i].to]=max(r[e[i].to],r[tmp]);
if(!in[e[i].to]) q.push(e[i].to);
}
}
}
int main()
{
int sz=32<<20;
char *p=(char*)malloc(sz)+sz;
__asm__("movl %0, %%esp\n"::"r"(p));
scanf("%d",&T);
while(T--)
{
num=Num=Time=block=tot=0;
scanf("%d",&n);
memset(dfn,0,sizeof(int)*(n<<2));
memset(low,0,sizeof(int)*(n<<2));
memset(head,0,sizeof(int)*(n<<2));
memset(Head,0,sizeof(int)*(n<<2));
memset(left,0,sizeof(int)*(n<<2));
memset(right,0,sizeof(int)*(n<<2));
for(int i=1;i<=n;i++)
scanf("%lld%lld",&bomb[i].x,&bomb[i].r),bomb[i].id=i;
sort(bomb+1,bomb+n+1);
for(int i=1;i<=n;i++)
tmp[i]=bomb[i].x;
build(1,n,1);
for(int i=1;i<=n;i++)
{
int l=lower_bound(tmp+1,tmp+n+1,tmp[i]-bomb[i].r)-tmp;
int r=upper_bound(tmp+1,tmp+n+1,tmp[i]+bomb[i].r)-tmp-1;
if(l!=r) insert(l,r,1,n,1,pos[i]);
}
for(int i=1;i<=tot;i++)
if(!dfn[i]) dfs(i);
for(int i=1;i<=tot;i++)
for(int j=head[i];j;j=a[j].nxt)
if(belong[i]!=belong[a[j].to]) Add(belong[i],belong[a[j].to]),in[belong[a[j].to]]++;
topologysort();
for(int i=1;i<=n;i++)
ans[bomb[i].id]=r[belong[pos[i]]]-l[belong[pos[i]]]+1;
for(int i=1;i<n;i++)
printf("%d ",ans[i]);
printf("%d\n",ans[n]);
}
return 0;
}
犯下的错误:将每个实点的贡献设为1,拓扑排序时累加贡献,导致贡献重复累加。
输入:
1
5
48 4
8 3
-5 4
10 1
13 5
正确答案:
1 2 1 1 3
错误答案:
1 2 1 1 4