2019.7.10 义乌模拟赛 T4 D
考试的时候写了个常数大的要命的代码。然后思维复杂度不知道比std高多少。
首先我们知道一个结论:联通块个数等于点数-边数。
点数是平凡的,我们考虑边数。
边实质上是一个三维数点,三只log显然不行。
考虑对边差分,所以有\([1,r]\)减去\([1,l-1]\),这样是二维数点两个log有70分。
发现点的编号和边的编号是一样的,这肯定有什么特殊性质。
以\([1,r]\)为例,我们考虑什么时候第\(i\)条边\((x_i,y_i)\)有用。
显然至少要在\(\max(i,x_i,y_i)\)条边加入,且区间左端点大于等于\(\min(x_i,y_i)\)才能行。这个显然二维偏序扫描线+树状数组一个log。
\([1,l-1]\)也就同理了。时间复杂度\(O(nlogn)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 1000000
#define M 30
#define mod 1000000007
#define eps (1e-7)
#define U unsigned int
#define IT set<ques>::iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
using namespace std;
int n,m,A[N+5],B[N+5],Minn[N+5],Maxn[N+5],x[N+5],y[N+5],Ans[N+5],F[N+5],now,cnt;vector<int> Gs[N+5],G[N+5];
I void read(int &x){
char s=getchar();x=0;while(s<'0'||s>'9') s=Gc();
while(s>='0'&&s<='9') x=x*10+s-48,s=Gc();
}
I void print(int x){x>9&&(print(x/10),0);putchar(x%10+48);}
I void get(int x,int y){while(x<=n) F[x]+=y,x+=x&-x;}
I int find(int x){int ans=0;while(x) ans+=F[x],x-=x&-x;return ans;}
int main(){
freopen("d.in","r",stdin);freopen("d.out","w",stdout);
re int i,j;scanf("%d%d",&n,&m);for(i=1;i<n;i++) read(A[i]),read(B[i]),Maxn[i]=max(A[i],B[i]),Minn[i]=min(A[i],B[i]);for(i=1;i<=m;i++)read(x[i]),read(y[i]),Ans[i]=y[i]-x[i]+1;
for(i=1;i<=m;i++) G[x[i]-1].push_back(i);for(i=1;i<n;i++){
Minn[i]>=i+1&&(get(Maxn[i],1),Gs[Minn[i]].push_back(i),0);for(j=0;j<Gs[i].size();j++) get(Maxn[Gs[i][j]],-1);
for(j=0;j<G[i].size();j++)now=G[i][j],Ans[now]+=find(y[now]);
}for(i=1;i<=n;i++) Gs[i].clear(),G[i].clear();Me(F,0);
for(i=1;i<=m;i++) G[y[i]].push_back(i);for(i=1;i<n;i++) Gs[max(Maxn[i],i)].push_back(i);
for(i=1;i<=n;i++){
for(j=0;j<Gs[i].size();j++) get(Minn[Gs[i][j]],1),cnt++;for(j=0;j<G[i].size();j++) now=G[i][j],Ans[now]-=cnt-find(x[now]-1);
}for(i=1;i<=m;i++) print(Ans[i]),printf("\n");
}