學長測試題原題。
題解:神奇的線段樹優化建圖+拓撲排序。
對於一個地雷來說,我們可以在將所有地雷按照座標排序後二分得到所能引爆的地雷區間,然後從可以被其引爆的地雷向其連單向邊,這個可以用線段樹優化建圖來搞;爲了統計答案,我們還要維護每個點所能控制的區間端點的位置。最後用拓撲排序更新每個點所能引爆的最大區間,統計答案即可。
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