zoukankan      html  css  js  c++  java
  • CF1093E Intersection of Permutations

    III.CF1093E Intersection of Permutations

    首先,我们如果令\(c[i]\)表示\(b[i]\)在数组\(a\)中出现的位置,

    那么对于一次询问,答案就是\(c\)中下标在\([l_2,r_2]\)间的数字中,值位于\([l_1,r_1]\)间的数量。

    思路1.树状数组套权值线段树。

    很明显,这题的操作如果不带修可以用主席树完成(下标在\([l_2,r_2]\)里面的树上进行值域查询)。带了修改,则套上树状数组完事。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define mid ((l+r)>>1)
    int n,m,root[200100],cnt,bin[10010000],tp,num[200100],a[200100],b[200100];
    struct node{
    	int lson,rson,sum;
    }seg[20001000];
    int newid(){
    	if(!tp)return ++cnt;
    	return bin[tp--];
    }
    void ADD(int &x,int l,int r,int P,int val){
    	if(l>P||r<P)return;
    	if(!x)x=newid();
    	seg[x].sum+=val;
    	if(l!=r)ADD(seg[x].lson,l,mid,P,val),ADD(seg[x].rson,mid+1,r,P,val);
    	if(!seg[x].sum)bin[++tp]=x,x=0;
    }
    void add(int x,int y,int z){
    	while(x<=n)ADD(root[x],1,n,y,z),x+=x&-x;
    }
    int Query(int x,int l,int r,int L,int R){
    	if(!x||l>R||r<L)return 0;
    	if(L<=l&&r<=R)return seg[x].sum;
    	return Query(seg[x].lson,l,mid,L,R)+Query(seg[x].rson,mid+1,r,L,R);
    }
    int ask(int l,int r,int L,int R){
    	l--;
    	int res=0;
    	while(r)res+=Query(root[r],1,n,L,R),r-=r&-r;
    	while(l)res-=Query(root[l],1,n,L,R),l-=l&-l;
    	return res;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,x;i<=n;i++)scanf("%d",&x),a[x]=i;
    	for(int i=1,x;i<=n;i++)scanf("%d",&x),b[i]=a[x];
    	for(int i=1;i<=n;i++)add(i,b[i],1);
    	for(int i=1,t1,t2,t3,t4,t5;i<=m;i++){
    		scanf("%d",&t1);
    		if(t1==1)scanf("%d%d%d%d",&t2,&t3,&t4,&t5),printf("%d\n",ask(t4,t5,t2,t3));
    		else scanf("%d%d",&t2,&t3),add(t2,b[t2],-1),add(t3,b[t3],-1),swap(b[t2],b[t3]),add(t2,b[t2],1),add(t3,b[t3],1);
    	}
    	return 0;
    }
    

    思路2.奇奇怪怪的分块-ver1.0

    我们在数组\(c\)上分块,在每个块内排序。每次整块二分,散块直接暴力,复杂度\(O(n\sqrt{n}\log n)\)

    因为实现很糟糕,因此它T掉了。

    TLE代码:

    #pragma GCC optimize(3)
    #include<bits/stdc++.h>
    using namespace std;
    const int BBB=600;
    int n,m,BLK[200100],ra[200100],c[200100],d[200100],lp[1000],lim;
    inline void SWAP(int x,int y){
    	swap(c[x],c[y]);
    	for(register int i=lp[BLK[x]];i<lp[BLK[x]+1];i++)d[i]=c[i];
    	sort(d+lp[BLK[x]],d+lp[BLK[x]+1]);
    	for(register int i=lp[BLK[y]];i<lp[BLK[y]+1];i++)d[i]=c[i];
    	sort(d+lp[BLK[y]],d+lp[BLK[y]+1]);
    }
    inline int CALC(int l,int r,int L,int R){
    	register int res=0;
    	if(BLK[L]==BLK[R]){
    		for(register int i=L;i<R;i++)res+=(c[i]>=l&&c[i]<r);
    		return res;
    	}
    	for(register int i=L;i<lp[BLK[L]+1];i++)res+=(c[i]>=l&&c[i]<r);
    	for(register int i=lp[BLK[R]];i<R;i++)res+=(c[i]>=l&&c[i]<r);
    	for(register int i=BLK[L]+1;i<BLK[R];i++)res+=lower_bound(d+lp[i],d+lp[i+1],r)-lower_bound(d+lp[i],d+lp[i+1],l);
    	return res; 
    }
    inline void read(int &x){
    	x=0;
    	register char c=getchar();
    	while(c>'9'||c<'0')c=getchar();
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    }
    inline int read(){
    	register int x=0;
    	register char c=getchar();
    	while(c>'9'||c<'0')c=getchar();
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x;
    }
    inline void print(int x){
    	if(x<=9)putchar('0'+x);
    	else print(x/10),putchar('0'+x%10);
    }
    int main(){
    	read(n),read(m);
    	for(register int i=0;i<n;i++)ra[read()]=i,BLK[i]=i/BBB;
    	for(register int i=0;i<n;i++)c[i]=d[i]=ra[read()];
    	lim=BLK[n-1]+1;
    	for(register int i=0;i<lim;i++)lp[i]=i*BBB;
    	lp[lim]=n,BLK[n]=lim;
    	for(register int i=0;i<lim;i++)sort(d+lp[i],d+lp[i+1]);
    	for(register int i=1,t1,t2,t3,t4,t5;i<=m;i++){
    		read(t1);
    		if(t1==1)read(t2),read(t3),read(t4),read(t5),t2--,t4--,print(CALC(t2,t3,t4,t5)),putchar('\n');
    		else read(t2),read(t3),t2--,t3--,SWAP(t2,t3);
    	}
    	return 0;
    }
    

    思路3.奇奇怪怪的分块-ver2.0

    设块长为\(k\),我们分析一下各个操作的复杂度:

    整块复杂度为\(O(k\log\dfrac{n}{k})=O(k\log n)\)

    散块复杂度为\(O(\dfrac{n}{k})\)

    修改复杂度为\(O(\dfrac{n}{k}\log\dfrac{n}{k})=\dfrac{n}{k}\log n\)

    则当\(k=\sqrt{n}\)时复杂度最优,为\(O(\sqrt{n}\log n)\)

    我们如果只考虑询问复杂度的话,这个\(k\)是可以取到\(\sqrt{n\log n}\)的。关键是修改。

    有两种思路。

    1. 考虑将修改也分块。将修改储存下来,每累计\(\sqrt{n\log n}\)个修改后,就暴力\(O(n\log n)\)全部推平重新排序。则每次询问时,只需要\(O(\sqrt{n\log n})\)遍历所有修改并更新答案即可。复杂度\(O(\sqrt{n\log n})\)

    2. 原本的修改,我们是\(\dfrac{n}{k}\log n\)重新排序的;发现修改只作用于一个位置;故可以直接\(O(k)\)插入排序,或者直接套上std::set让修改优化到\(O(\log k)\)。(实际上,套上set后就已经相当于一种树套树了,是分块套平衡树)

    代码:

    #pragma GCC optimize(3)
    #include<bits/stdc++.h>
    using namespace std;
    const int BBB=1000;
    int n,m,BLK[200100],ra[200100],c[200100],d[200100],lp[1000],lim;
    vector<pair<int,int> >v;
    inline int CALC(int l,int r,int L,int R){
    	register int res=0;
    	for(auto x:v){
    		res-=(x.first>=L)&&(x.first<R)&&(c[x.first]>=l)&&(c[x.first]<r);
    		res-=(x.second>=L)&&(x.second<R)&&(c[x.second]>=l)&&(c[x.second]<r);
    		swap(c[x.first],c[x.second]);
    		res+=(x.first>=L)&&(x.first<R)&&(c[x.first]>=l)&&(c[x.first]<r);
    		res+=(x.second>=L)&&(x.second<R)&&(c[x.second]>=l)&&(c[x.second]<r);
    	}
    	for(int i=v.size()-1;i>=0;i--)swap(c[v[i].first],c[v[i].second]);
    	if(BLK[L]==BLK[R]){
    		for(register int i=L;i<R;i++)res+=(c[i]>=l&&c[i]<r);
    		return res;
    	}
    	for(register int i=L;i<lp[BLK[L]+1];i++)res+=(c[i]>=l&&c[i]<r);
    	for(register int i=lp[BLK[R]];i<R;i++)res+=(c[i]>=l&&c[i]<r);
    	for(register int i=BLK[L]+1;i<BLK[R];i++)res+=lower_bound(d+lp[i],d+lp[i+1],r)-lower_bound(d+lp[i],d+lp[i+1],l);
    	return res; 
    }
    void init(){
    	for(auto x:v)swap(c[x.first],c[x.second]);
    	v.clear();
    	for(int i=0;i<n;i++)d[i]=c[i];
    	for(int i=0;i<lim;i++)sort(d+lp[i],d+lp[i+1]);
    }
    inline void read(int &x){
    	x=0;
    	register char c=getchar();
    	while(c>'9'||c<'0')c=getchar();
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    }
    inline int read(){
    	register int x=0;
    	register char c=getchar();
    	while(c>'9'||c<'0')c=getchar();
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x;
    }
    inline void print(int x){
    	if(x<=9)putchar('0'+x);
    	else print(x/10),putchar('0'+x%10);
    }
    int main(){
    	read(n),read(m);
    	for(register int i=0;i<n;i++)ra[read()]=i,BLK[i]=i/BBB;
    	for(register int i=0;i<n;i++)c[i]=ra[read()];
    	lim=BLK[n-1]+1;
    	for(register int i=0;i<lim;i++)lp[i]=i*BBB;
    	lp[lim]=n,BLK[n]=lim;
    	init();
    	for(register int i=1,t1,t2,t3,t4,t5;i<=m;i++){
    		read(t1);
    		if(t1==1)read(t2),read(t3),read(t4),read(t5),t2--,t4--,print(CALC(t2,t3,t4,t5)),putchar('\n');
    		else read(t2),read(t3),t2--,t3--,v.push_back(make_pair(t2,t3));
    		if(v.size()>=BBB)init();
    	}
    	return 0;
    }
    

  • 相关阅读:
    You can’t access this shared folder because your organization’s security policies block unauthenticated guest access
    iOS 程序退出之后
    给网站得标签头加上小图标
    ios外界蓝牙适配
    JavaScript&jquery判断下载地址是否可用,同时判断浏览器是否禁用弹窗
    GCD初级使用
    find与xargs
    sed命令详解
    shell变量详解
    date命令详解
  • 原文地址:https://www.cnblogs.com/Troverld/p/14611078.html
Copyright © 2011-2022 走看看