ZOJ 4008.Yet Another Tree Query Problem(问题模型转化+线段树离线处理)
题目链接
题解思路:题目给了一棵n个节点的数和q次询问,每次询问是询问[L,R]区间内有多少个连通块。由于是一棵树,连通块的数量=节点数-边数,在明白这点以后就可以开始思考了,节点数很简单,就等于R-L+1,那么最重要的就是求边数。由于q有2e5次询问,因此每次算边数必须在log一下的时间内计算。这里我们可以将问题转化,不再是求[L,R]内连通块的数量,而是去算总共n-1条边,有多少恰好被[L,R]包含。这时问题便简单了,我们只需要将每条边和询问先按右区间排序,再将同样右区间中的询问放在最后面,每经过一条边ans++,最后再减去左区间<L的边数即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> PII; #define ls l,mid,rt<<1 #define rs mid+1,r,rt<<1|1 #define endl '\n' #define p4 puts("444") const int MAXN = 1e6+10; const double EPS = 1e-12; int n,q; int ans[MAXN],sum[MAXN]; struct node{ int l,r,sx,id; }a[MAXN]; bool cmp(node aa,node bb){ if(aa.r!=bb.r)return aa.r<bb.r; else return aa.sx<bb.sx; } void Build(int l,int r,int rt){ if(l==r){ sum[rt]=0; return ; } int mid=(l+r)/2; Build(ls); Build(rs); sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void Update(int l,int r,int rt,int pos){ if(pos==l&&l==r){ sum[rt]++; return ; } int mid=(l+r)/2; if(pos<=mid)Update(ls,pos); else Update(rs,pos); sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } int Query(int l,int r,int rt,int pos){ if(pos==0)return 0; if(pos>=r)return sum[rt]; int mid=(l+r)/2,ans=0; ans+=Query(ls,pos); if(pos>mid)ans+=Query(rs,pos); return ans; } void solve(){ scanf("%d %d",&n,&q); Build(1,n,1); for(int i=1;i<n+q;i++){ scanf("%d %d",&a[i].l,&a[i].r); if(a[i].l>a[i].r)swap(a[i].l,a[i].r); a[i].id=i; if(i<n)a[i].sx=0; else a[i].sx=1; } sort(a+1,a+n+q,cmp); int now=0; for(int i=1;i<n+q;i++){ if(!a[i].sx){ now++; Update(1,n,1,a[i].l); } else { ans[a[i].id]=(a[i].r-a[i].l+1)-(now-Query(1,n,1,a[i].l-1)); //cout<<" ### "<<now<<" "<<Query(1,n,1,a[i].l-1)<<endl; } } for(int i=n;i<n+q;i++)printf("%d\n",ans[i]); } int main() { int T=1; scanf("%d",&T); while(T--) solve(); }
希望用自己的努力为自己赢得荣誉。

浙公网安备 33010602011771号