题目大意:
对于一个
n(≤10w)
个点,
m(≤20w)
条边的无向图,有
q(≤10w)
次询问:只保留编号为
L
~
题解:
开始觉得
L
~
对这道题是否有种似曾相识的感觉呢?
我们曾做过一题叫 【bzoj3514】Codechef MARCH14 GERALD07加强版 。
题目几乎一样,不过每次询问保留的是编号在
L
~
那题我们被动的预处理
i∈[L,R]
对答案有贡献当且仅当……
这题我们主动的保留对答案有影响的边。
于是LCT+BIT。
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<iostream>
#include<cassert>
#include<algorithm>
#define pi acos(-1)
#define inf (1<<30)
#define INF (1<<62)
#define CLR(x,f) memset(x,f,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define prt(x) cout<<" "<<#x<<":"<<x<<endl
#define huh(x) printf("--------DEBUG(%d)--------\n",x)
using namespace std;
typedef long long ll;
const int M=100005;
int n,m,q;
struct Edge{
Edge(){}
Edge(int u_,int v_):u(u_),v(v_){}
int u,v;
bool operator<(const Edge &a)const{
if(v!=a.v)return v<a.v;
return u>a.u;
}
}edge[M<<1];
int fa[M];
int get(int x){
return fa[x]==x?x:fa[x]=get(fa[x]);
}
struct BIT{
int C[M];
void init(){
memset(C,0,n+1<<2);
}
void update(int x,int f){
for(;x;x-=x&-x)C[x]+=f;
}
int query(int x){
int res=0;
for(;x<=n;x+=x&-x)res+=C[x];
return res;
}
}bit;
struct LCT{
static const int N=M*3;
int fa[N],c[N][2],v[N],mn[N];
char rev[N];
void init(){
for(int i=0;i<=n+m;i++){
c[i][0]=c[i][1]=fa[i]=0;
v[i]=inf;mn[i]=i;
}
}
bool isroot(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
int cmp(int x,int y){
return v[x]<v[y]?x:y;
}
void up(int x){
mn[x]=cmp(cmp(mn[c[x][0]],mn[c[x][1]]),x);
}
int query(int x,int y){
makeroot(x);access(y);splay(y);
return mn[y];
}
void rotate(int x){
int y=fa[x],z=fa[y],l,r;
if(c[y][0]==x)l=0;else l=1;r=l^1;
if(!isroot(y)){
if(c[z][0]==y)c[z][0]=x;
else c[z][1]=x;
}
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
up(y);
}
int q[N];
void down(int x){
if(rev[x]){
rev[c[x][0]]^=1;
rev[c[x][1]]^=1;
swap(c[x][0],c[x][1]);
rev[x]=0;
}
}
void splay(int x){
int top=0;q[++top]=x;
for(int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
for(int i=top;i>=1;i--)down(q[i]);
while(!isroot(x)){
int y=fa[x],z=fa[y];
if(!isroot(y)){
if(c[z][0]==y^c[y][0]==x)rotate(x);
else rotate(y);
}
rotate(x);
}up(x);
}
void makeroot(int x){
access(x);splay(x);rev[x]^=1;
}
void access(int x){
for(int t=0;x;t=x,x=fa[x]){
splay(x);c[x][1]=t;up(x);
}
}
void cut(int x,int y){
makeroot(x);access(y);splay(y);
c[y][0]=fa[x]=0;
}
void link(int x,int y){
makeroot(x);fa[x]=y;
}
}lct;
int last[M],allc;
struct Query{
int to,l,nxt;
}que[M];
void ins(int l,int r,int id){
que[allc].l=l;
que[allc].to=id;
que[allc].nxt=last[r];
last[r]=allc++;
}
int ans[M];
void solve(){
int tmp=0;
for(int u,v,i=1;i<=m;i++){
scanf("%d%d",&u,&v);
if(u==v)continue;
if(u>v)swap(u,v);
edge[++tmp]=Edge(u,v);
}
m=tmp;
sort(edge+1,edge+m+1);
for(int i=1;i<=n;i++)fa[i]=i;
bit.init();lct.init();
memset(last,-1,n+1<<2);allc=0;
for(int l,r,i=1;i<=q;i++){
scanf("%d%d",&l,&r);
ins(l,r,i);
}
int pre=1;
for(int i=1;i<=n;i++){
for(;pre<=m&&edge[pre].v==i;pre++){
int u=edge[pre].u,v=edge[pre].v;
if(get(u)==get(v)){
int id=lct.query(u,v);id-=n;
if(edge[id].u>=u)continue;
lct.cut(edge[id].u,id+n);
lct.cut(edge[id].v,id+n);
bit.update(edge[id].u,-1);
lct.v[pre+n]=u;
lct.link(u,pre+n);
lct.link(v,pre+n);
bit.update(u,1);
}
else{
lct.v[pre+n]=u;
lct.link(u,pre+n);
lct.link(v,pre+n);
bit.update(u,1);
fa[get(u)]=get(v);
}
}
for(int j=last[i];j!=-1;j=que[j].nxt){
int l=que[j].l,to=que[j].to;
ans[to]=n-bit.query(l);
}
}
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
}
int main(){
while(~scanf("%d%d%d",&n,&m,&q))solve();
return 0;
}
提一下和官方题解不一样的官方标程做法:莫队+dsu。
以下是我研读之后的代码。
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<cstdio>
#include<bitset>
#include<cassert>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#define pi acos(-1)
#define inf (1<<30)
#define INF (1<<62)
#define y1 bflaisfnmasf
#define y2 fsafgmalg
#define tm afnsjkf
#define j1 sfakf
#define j2 fasndfkas
#define fi first
#define se second
#define CLR(x,f) memset(x,f,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define prt(x) cout<<#x<<":"<<x<<" "
#define prtn(x) cout<<#x<<":"<<x<<endl
#define huh(x) printf("--------case(%d)--------\n",x)
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const int M=100005;
int n,m,Q;
int allc,last[M];
ll deg[M];
struct Edge{
Edge(){}
Edge(int u_,int v_):to(u_),nxt(v_){}
int to,nxt;
}edge[M<<2];
void ins(int u,int v){
deg[u]++;
edge[allc]=Edge(v,last[u]);
last[u]=allc++;
}
int ans;
int fa[M],di[M],tcnt;
ii tar[M<<1];
int getFa(int x){
while(fa[x]!=x)x=fa[x];return x;
}
void addin(int l,int r,int lim){
int tcnt=0;
for(int i=l;i<=r;i++){
for(int j=last[i];j!=-1;j=edge[j].nxt){
int to=edge[j].to;
if(to<=i||to>lim)continue;
int x=getFa(i),y=getFa(to);
if(x!=y){
ans--;
if(di[x]<di[y]){
tar[++tcnt]=ii(x,di[x]);
fa[x]=y;
}
else{
tar[++tcnt]=ii(y,di[y]);
fa[y]=x;
if(di[x]==di[y]){
tar[++tcnt]=ii(x,di[x]);
di[x]++;
}
}
}
}
}
for(;tcnt;tcnt--){
fa[tar[tcnt].fi]=tar[tcnt].fi;
di[tar[tcnt].fi]=tar[tcnt].se;
}
}
int bel[M];
struct ques{
int l,r,id;
inline bool operator<(const ques &a)const{
if(bel[l]!=bel[a.l])return bel[l]<bel[a.l];
return r<a.r;
}
}q[M];
int res[M];
int R[M],tot;
void rd(int &r){
r=0;char c;
while(c=getchar(),c<48);
do r=r*10+(c^48);
while(c=getchar(),c>47);
}
int kase;
void solve(){
// huh(++kase);
allc=0;for(int i=1;i<=n;i++)deg[i]=0,last[i]=-1;
for(int u,v,i=1;i<=m;i++){
rd(u);rd(v);
ins(u,v);ins(v,u);
}
for(int i=1;i<=Q;i++){
rd(q[i].l);rd(q[i].r);q[i].id=i;
}
tot=0;
for(int i=1;i<=n;i++)deg[i]+=deg[i-1];
int lim;if(n<=1000&&m<=1000)lim=30;else lim=800;//:P
while(R[tot]<n){
tot++;R[tot]=R[tot-1]+1;
while(R[tot]<=n&°[R[tot]]-deg[R[tot-1]]<=lim&&R[tot]-R[tot-1]<=lim)R[tot]++;
R[tot]=max(R[tot]-1,R[tot-1]+1);
}
for(int i=1;i<=tot;i++)
for(int j=R[i-1]+1;j<=R[i];j++)
bel[j]=i;
sort(q+1,q+Q+1);
for(int now=1,i=1;now<=Q&&i<=tot;i++){//ith block
if(bel[q[now].l]>i)continue;
for(int j=1;j<=n;j++)fa[j]=j,di[j]=1;
ans=n;
while(now<=Q&&bel[q[now].r]==i){
addin(q[now].l,q[now].r,q[now].r);
res[q[now].id]=ans;ans=n;now++;
}
for(int j=R[i]+1;bel[q[now].l]==i&&j<=n;j++){
for(int k=last[j];k!=-1;k=edge[k].nxt){
int to=edge[k].to;
if(to<=R[i]||to>j)continue;
int x=getFa(j),y=getFa(to);
if(x!=y){
ans--;
if(di[x]<di[y])fa[x]=y;
else{
fa[y]=x;
if(di[x]==di[y])di[x]++;
}
}
}
int preans=ans;
while(now<=Q&&bel[q[now].l]==i&&q[now].r==j){
addin(q[now].l,R[i],q[now].r);res[q[now].id]=ans;
ans=preans;now++;
}
}
}
for(int i=1;i<=Q;i++)
printf("%d\n",res[i]);
}
int main(){
freopen("1007.in","r",stdin);
freopen("1007.ans","w",stdout);
while(~scanf("%d%d%d",&n,&m,&Q))solve();
return 0;
}
再回顾标程,发现自己对于小细节上的优化没有以前敏感了,枚举边的时候固定的那一端不用每次都getFa(),合并的时候顺便更新一下即可(如标程)。
依然对标程用
q[now].l−1
判定表示不太理解。
最后超时真是不知所措,改成和标程一模一样就没意思了,所以先就这样吧。<( ̄︶ ̄)>