1. 程式人生 > >[BZOJ4399]魔法少女LJJ

[BZOJ4399]魔法少女LJJ

mes 能說 我認 .cn color ges 改變 建立 tdi

題目大意:給你一個圖,有一些操作要你實現。具體操作見題目。

解題思路:首先,我認真地讀著題目,當我看到

技術分享

這兩行時,感覺有點不知所措。然而,

技術分享

!!!

這充分說明了出題者腦洞之大。orz

當我歷經千辛萬苦寫完代碼打算測樣例時,發現樣例第11行出現了個操作9!而且還影響答案!相當於這題連樣例都沒有。

吐槽完了,回歸正題。這道題不難看出,需要用線段樹合並來做。並且在建立權值線段樹之前,先得把數據離散化(數據<=1000000000)。

對於操作3和4,刪除所有需要改變的節點,然後加進權值x裏即可。

對於操作6,可以用$\log_{}ab=\log_{}a+\log_{}b$的方式進行比較。

其他操作隨便搞搞就行了。

數組不要開太小,當然也不能開太大。

C++ Code:

#include<cstdio>
#include<algorithm>
#include<map>
#include<cmath>
#define N 6000000
using namespace std;
int m,n=0,mx=0,node=0,u,v,x,cnt;
int fa[400001],root[400001],ld[N],rd[N],mp[400001],d[N];
double lg[N]={0};
struct operation{
	int c,a,b;
}ol[400001];
map<int,int>p;
inline void update(int k){
	d[k]=d[ld[k]]+d[rd[k]];
	lg[k]=lg[ld[k]]+lg[rd[k]];
}
int dad(int x){return fa[x]==x?x:(fa[x]=dad(fa[x]));}
void add(int& k,int l,int r,int x,int num){
	if(!k)k=++node;
	if(l==r){
		d[k]+=num;
		lg[k]+=num*log(mp[x]);
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)add(ld[k],l,mid,x,num);else
	add(rd[k],mid+1,r,x,num);
	update(k);
}
inline int merge(int u,int v,int l,int r){
	if(!u||!v)return u|v;
	if(l==r){
		d[u]+=d[v];
		lg[u]+=lg[v];
		return u;
	}
	int mid=l+r>>1;
	ld[u]=merge(ld[u],ld[v],l,mid);
	rd[u]=merge(rd[u],rd[v],mid+1,r);
	update(u);
	return u;
}
int query(int k,int l,int r,int L,int R){
	if(L<=l&&r<=R)return d[k];
	int m=l+r>>1,ans=0;
	if(L<=m)ans+=query(ld[k],l,m,L,R);
	if(m<R)ans+=query(rd[k],m+1,r,L,R);
	return ans;
}
void del(int k,int l,int r,int L,int R){
	if(!d[k])return;
	if(l==r){
		d[k]=0;
		lg[k]=0;
		return;
	}
	int m=l+r>>1;
	if(L<=m)del(ld[k],l,m,L,R);
	if(m<R)del(rd[k],m+1,r,L,R);
	update(k);
}
int ask(int k,int l,int r,int sum){
	if(l==r)return l;
	int mid=l+r>>1;
	if(sum<=d[ld[k]])return ask(ld[k],l,mid,sum);
	return ask(rd[k],mid+1,r,sum-d[ld[k]]);
}
int main(){
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		scanf("%d",&ol[i].c);
		if(ol[i].c==1){
			scanf("%d",&ol[i].a);
			mp[++mx]=ol[i].a;
		}else
		if(ol[i].c==3||ol[i].c==4){
			scanf("%d%d",&ol[i].a,&ol[i].b);
			mp[++mx]=ol[i].b;
		}else
		if(ol[i].c==2||ol[i].c==5||ol[i].c==6)scanf("%d%d",&ol[i].a,&ol[i].b);else
		scanf("%d",&ol[i].a);
	}
	sort(mp+1,mp+mx+1);
	mx=unique(mp+1,mp+mx+1)-mp-1;
	for(int i=1;i<=mx;i++)p[mp[i]]=i;
	for(int i=1;i<=m;i++){
		if(ol[i].c==1){
			++n;
			fa[n]=n;
			add(root[n],1,mx,p[ol[i].a],1);
		}else
		if(ol[i].c==2){
			u=dad(ol[i].a),v=dad(ol[i].b);
			if(u!=v){
				fa[v]=u;
				root[u]=merge(root[u],root[v],1,mx);
			}
		}else
		if(ol[i].c==3){
			u=dad(ol[i].a),x=p[ol[i].b];
			if(x==1)continue;
			cnt=query(root[u],1,mx,1,x-1);
			add(root[u],1,mx,x,cnt);
			del(root[u],1,mx,1,x-1);
		}else
		if(ol[i].c==4){
			u=dad(ol[i].a),x=p[ol[i].b];
			if(x==mx)continue;
			cnt=query(root[u],1,mx,x+1,mx);
			add(root[u],1,mx,x,cnt);
			del(root[u],1,mx,x+1,mx);
		}else
		if(ol[i].c==5){
			u=dad(ol[i].a);
			printf("%d\n",mp[ask(root[u],1,mx,ol[i].b)]);
		}else
		if(ol[i].c==6){
			u=dad(ol[i].a),v=dad(ol[i].b);
			printf("%d\n",lg[root[u]]>lg[root[v]]?1:0);
		}else
		if(ol[i].c==7)
		printf("%d\n",d[root[dad(ol[i].a)]]);
	}
	return 0;
}

然而我看到最快的人的耗時才1800+ms,我卻有18000+ms,就感覺不爽,於是加了讀入優化(比平常的讀優更高級)。

C++ Code:

#include<cstdio>
#include<algorithm>
#include<map>
#include<cmath>
#include<cctype>
#define N 6000000
using namespace std;
int m,n=0,mx=0,node=0,u,v,x,cnt;
int fa[400001],root[400001],ld[N],rd[N],mp[400001],d[N];
double lg[N]={0};
char buf[30000020];
int bufpos;
inline void init(){
	buf[fread(buf,1,30000020,stdin)]=‘\0‘;
    bufpos=0;
}
inline int readint(){
	int p=0;
	for(;!isdigit(buf[bufpos]);bufpos++);
	for(;isdigit(buf[bufpos]);bufpos++)
	p=p*10+buf[bufpos]-‘0‘;
	return p;
}
struct operation{
	int c,a,b;
}ol[400001];
map<int,int>p;
inline void update(int k){
	d[k]=d[ld[k]]+d[rd[k]];
	lg[k]=lg[ld[k]]+lg[rd[k]];
}
int dad(int x){return fa[x]==x?x:(fa[x]=dad(fa[x]));}
void add(int& k,int l,int r,int x,int num){
	if(!k)k=++node;
	if(l==r){
		d[k]+=num;
		lg[k]+=num*log(mp[x]);
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)add(ld[k],l,mid,x,num);else
	add(rd[k],mid+1,r,x,num);
	update(k);
}
inline int merge(int u,int v,int l,int r){
	if(!u||!v)return u|v;
	if(l==r){
		d[u]+=d[v];
		lg[u]+=lg[v];
		return u;
	}
	int mid=l+r>>1;
	ld[u]=merge(ld[u],ld[v],l,mid);
	rd[u]=merge(rd[u],rd[v],mid+1,r);
	update(u);
	return u;
}
int query(int k,int l,int r,int L,int R){
	if(L<=l&&r<=R)return d[k];
	int m=l+r>>1,ans=0;
	if(L<=m)ans+=query(ld[k],l,m,L,R);
	if(m<R)ans+=query(rd[k],m+1,r,L,R);
	return ans;
}
void del(int k,int l,int r,int L,int R){
	if(!d[k])return;
	if(l==r){
		d[k]=0;
		lg[k]=0;
		return;
	}
	int m=l+r>>1;
	if(L<=m)del(ld[k],l,m,L,R);
	if(m<R)del(rd[k],m+1,r,L,R);
	update(k);
}
int ask(int k,int l,int r,int sum){
	if(l==r)return l;
	int mid=l+r>>1;
	if(sum<=d[ld[k]])return ask(ld[k],l,mid,sum);
	return ask(rd[k],mid+1,r,sum-d[ld[k]]);
}
int main(){
	init();
	m=readint();
	for(int i=1;i<=m;i++){
		ol[i].c=readint();
		if(ol[i].c==1)mp[++mx]=ol[i].a=readint();else
		if(ol[i].c==3||ol[i].c==4){
			ol[i].a=readint();
			mp[++mx]=ol[i].b=readint();
		}else
		if(ol[i].c==2||ol[i].c==5||ol[i].c==6)ol[i].a=readint(),ol[i].b=readint();else
		ol[i].a=readint();
	}
	sort(mp+1,mp+mx+1);
	mx=unique(mp+1,mp+mx+1)-mp-1;
	for(int i=1;i<=mx;i++)p[mp[i]]=i;
	for(int i=1;i<=m;i++){
		if(ol[i].c==1){
			++n;
			fa[n]=n;
			add(root[n],1,mx,p[ol[i].a],1);
		}else
		if(ol[i].c==2){
			u=dad(ol[i].a),v=dad(ol[i].b);
			if(u!=v){
				fa[v]=u;
				root[u]=merge(root[u],root[v],1,mx);
			}
		}else
		if(ol[i].c==3){
			u=dad(ol[i].a),x=p[ol[i].b];
			if(x==1)continue;
			cnt=query(root[u],1,mx,1,x-1);
			add(root[u],1,mx,x,cnt);
			del(root[u],1,mx,1,x-1);
		}else
		if(ol[i].c==4){
			u=dad(ol[i].a),x=p[ol[i].b];
			if(x==mx)continue;
			cnt=query(root[u],1,mx,x+1,mx);
			add(root[u],1,mx,x,cnt);
			del(root[u],1,mx,x+1,mx);
		}else
		if(ol[i].c==5){
			u=dad(ol[i].a);
			printf("%d\n",mp[ask(root[u],1,mx,ol[i].b)]);
		}else
		if(ol[i].c==6){
			u=dad(ol[i].a),v=dad(ol[i].b);
			printf("%d\n",lg[root[u]]>lg[root[v]]?1:0);
		}else
		if(ol[i].c==7)
		printf("%d\n",d[root[dad(ol[i].a)]]);
	}
	return 0;
}

技術分享

(上面是讀優)快了3000ms左右,然而還是好慢。

不過能說明在數據較大的題目裏,讀入優化能大大提高代碼效率。

那個1800+ms的是怎麽做到的?orz

後來我又想到map可能會拖慢時間,於是map改成了直接用lower_bound()找。又快了2500ms左右。

技術分享

C++ Code:

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cctype>
#define N 6000000
using namespace std;
int m,n=0,mx=0,node=0,u,v,x,cnt;
int fa[400001],root[400001],ld[N],rd[N],mp[400001],d[N];
double lg[N]={0};
char buf[30000020];
int bufpos;
inline int fnd(int x){
	return lower_bound(mp+1,mp+1+mx,x)-mp;
}
inline void init(){
	buf[fread(buf,1,30000020,stdin)]=‘\0‘;
    bufpos=0;
}
inline int readint(){
	int p=0;
	for(;!isdigit(buf[bufpos]);bufpos++);
	for(;isdigit(buf[bufpos]);bufpos++)
	p=p*10+buf[bufpos]-‘0‘;
	return p;
}
struct operation{
	int c,a,b;
}ol[400001];
inline void update(int k){
	d[k]=d[ld[k]]+d[rd[k]];
	lg[k]=lg[ld[k]]+lg[rd[k]];
}
int dad(int x){return fa[x]==x?x:(fa[x]=dad(fa[x]));}
void add(int& k,int l,int r,int x,int num){
	if(!k)k=++node;
	if(l==r){
		d[k]+=num;
		lg[k]+=num*log(mp[x]);
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)add(ld[k],l,mid,x,num);else
	add(rd[k],mid+1,r,x,num);
	update(k);
}
inline int merge(int u,int v,int l,int r){
	if(!u||!v)return u|v;
	if(l==r){
		d[u]+=d[v];
		lg[u]+=lg[v];
		return u;
	}
	int mid=l+r>>1;
	ld[u]=merge(ld[u],ld[v],l,mid);
	rd[u]=merge(rd[u],rd[v],mid+1,r);
	update(u);
	return u;
}
int query(int k,int l,int r,int L,int R){
	if(L<=l&&r<=R)return d[k];
	int m=l+r>>1,ans=0;
	if(L<=m)ans+=query(ld[k],l,m,L,R);
	if(m<R)ans+=query(rd[k],m+1,r,L,R);
	return ans;
}
void del(int k,int l,int r,int L,int R){
	if(!d[k])return;
	if(l==r){
		d[k]=0;
		lg[k]=0;
		return;
	}
	int m=l+r>>1;
	if(L<=m)del(ld[k],l,m,L,R);
	if(m<R)del(rd[k],m+1,r,L,R);
	update(k);
}
int ask(int k,int l,int r,int sum){
	if(l==r)return l;
	int mid=l+r>>1;
	if(sum<=d[ld[k]])return ask(ld[k],l,mid,sum);
	return ask(rd[k],mid+1,r,sum-d[ld[k]]);
}
int main(){
	init();
	m=readint();
	for(int i=1;i<=m;i++){
		ol[i].c=readint();
		if(ol[i].c==1)mp[++mx]=ol[i].a=readint();else
		if(ol[i].c==3||ol[i].c==4){
			ol[i].a=readint();
			mp[++mx]=ol[i].b=readint();
		}else
		if(ol[i].c==2||ol[i].c==5||ol[i].c==6)ol[i].a=readint(),ol[i].b=readint();else
		ol[i].a=readint();
	}
	sort(mp+1,mp+mx+1);
	mx=unique(mp+1,mp+mx+1)-mp-1;
	for(int i=1;i<=m;i++){
		if(ol[i].c==1){
			++n;
			fa[n]=n;
			add(root[n],1,mx,fnd(ol[i].a),1);
		}else
		if(ol[i].c==2){
			u=dad(ol[i].a),v=dad(ol[i].b);
			if(u!=v){
				fa[v]=u;
				root[u]=merge(root[u],root[v],1,mx);
			}
		}else
		if(ol[i].c==3){
			u=dad(ol[i].a),x=fnd(ol[i].b);
			if(x==1)continue;
			cnt=query(root[u],1,mx,1,x-1);
			add(root[u],1,mx,x,cnt);
			del(root[u],1,mx,1,x-1);
		}else
		if(ol[i].c==4){
			u=dad(ol[i].a),x=fnd(ol[i].b);
			if(x==mx)continue;
			cnt=query(root[u],1,mx,x+1,mx);
			add(root[u],1,mx,x,cnt);
			del(root[u],1,mx,x+1,mx);
		}else
		if(ol[i].c==5){
			u=dad(ol[i].a);
			printf("%d\n",mp[ask(root[u],1,mx,ol[i].b)]);
		}else
		if(ol[i].c==6){
			u=dad(ol[i].a),v=dad(ol[i].b);
			printf("%d\n",lg[root[u]]>lg[root[v]]?1:0);
		}else
		if(ol[i].c==7)
		printf("%d\n",d[root[dad(ol[i].a)]]);
	}
	return 0;
}

  

[BZOJ4399]魔法少女LJJ