20200606分治算法總結

Goodbye Souvenir

給定長度爲nn的數組, 定義數字XX在[l,r][l,r]內的值爲數字XX在[l,r][l,r]內最後一次出現位置的下標減去第一次出現位置的下標
給定mm次詢問, 每次詢問有三個整數a, b, ca,b,c,詢問規則如下:
當a = 1a=1時, 將數組內第bb個元素更改爲cc
當a = 2a=2時, 求區間[b,c][b,c]所有數字的值的和

輸入:
第一行兩個整數n,mn,m
第二行nn個整數, 表示數組
第3-3 + m3−3+m行, 每行三個整數, 表示每次詢問

輸出:
對於每次a = 2a=2的詢問, 輸出一個整數表示答案

 

題解:對時間CDQ分治,把每一個數的權值設爲pos-pre,座標看作(i,pre)

這樣就是一個靜態2維數點問題,掃描線+樹狀數組即可

代碼:(寫得比較爛。。)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 100005
#define M 800005
#define LL long long
int n,m;
set<int> S[N];
set<int>::iterator it,it2;
struct node{
	int op,l,r,x,y,id;
	node(){}
	node(int a,int b,int c,int d,int e,int f){op=a;l=b;r=c;x=d;y=e;id=f;}
	//op=1 l,r,x
	//op=2 l,r,x,y,id l,x,y,flg(r)
	bool operator < (const node &t)const{
		return l<t.l||(l==t.l&&op<t.op);
	}
}e[M],q[M];
int tot,a[N],acnt;
LL tra[N],ans[N];
void update(int x,int k)
{
	x++;
	while(x<=n+1){tra[x]+=k;x+=(x&-x);}
}
LL getsum(int l,int r)
{
	LL ret=0;r++;
	while(r){ret+=tra[r];r-=(r&-r);}
	while(l){ret-=tra[l];l-=(l&-l);}
	return ret;
}
void solve(int l,int r)
{
	if(l==r)return;
	int mid=(l+r)>>1;
	solve(l,mid);solve(mid+1,r);
	int qcnt=0;
	for(int i=l;i<=mid;i++)
		if(e[i].op==1)q[++qcnt]=e[i];
	int ff=0;
	for(int i=mid+1;i<=r;i++)
		if(e[i].op==2){
			ff++;
			q[++qcnt]=e[i];q[qcnt].r=-1;
			q[++qcnt]=e[i];q[qcnt].l=q[qcnt].r;q[qcnt].r=1;
		}
	if(!ff)return;
	sort(q+1,q+qcnt+1);
	for(int i=1;i<=qcnt;i++){
		if(q[i].op==1)update(q[i].r,q[i].x);
		else ans[q[i].id]+=getsum(q[i].x,q[i].y)*q[i].r;
	}
	for(int i=1;i<=qcnt;i++)
		if(q[i].op==1)update(q[i].r,-q[i].x);
}
int main()
{
	int i,op,l,r,p,x;
	n=gi();m=gi();
	for(i=1;i<=n;i++)S[i].insert(0);
	for(i=1;i<=n;i++){a[i]=gi();S[a[i]].insert(i);}
	for(i=1;i<=n;i++){
		it=S[a[i]].lower_bound(i);it--;
		e[++tot]=node(1,i,*it,i-(*it),0,0);
	}
	int acnt=0;
	for(i=1;i<=m;i++){
		op=gi();
		if(op==1){
			p=gi();x=gi();
			
			it2=it=S[a[p]].lower_bound(p);it2--;it++;
			if(it!=S[a[p]].end()){
				e[++tot]=node(1,*it,p,p-*it,0,0);
				e[++tot]=node(1,*it,*it2,*it-*it2,0,0);
			}
			
			e[++tot]=node(1,p,*it2,*it2-p,0,0);
			S[a[p]].erase(p);
			a[p]=x;
			S[a[p]].insert(p);
			
			it2=it=S[a[p]].lower_bound(p);it2--;it++;
			if(it!=S[a[p]].end()){
				e[++tot]=node(1,*it,*it2,*it2-*it,0,0);
				e[++tot]=node(1,*it,p,*it-p,0,0);
			}
			
			e[++tot]=node(1,p,*it2,p-*it2,0,0);
		}
		else{
			l=gi();r=gi();
			e[++tot]=node(2,l-1,r,l,r,++acnt);
		}
	}
	solve(1,tot);
	for(i=1;i<=acnt;i++)
		printf("%lld\n",ans[i]);
}

 

 

 

Xor-Set

題解:動態開點線段Trie樹(大概也只適用於這道題吧)

用BFS從高到低確定答案的某一位,同時在兩棵線段Trie樹上移動節點

當某一個節點被完全標記了,此時答案中低位的所有情況都可以被取到

那麼這一部分的答案就是等差數列求和,首項就是已經確定了的高位的部分

複雜度應該是O(n*log10^18)

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define N 1000005
#define LOG 60
#define LL long long
inline LL gi()
{
	char c;LL num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+1ll*c-48;c=getchar();}
	return num*flg;
}
const int mod=998244353;
const int inv2=499122177;
struct SegTrie{
	int ch[N][2],tot,rt;
	bool flg[N];
	void insert(int &i,LL l,LL r,LL ql,LL qr){
		if(!i)i=++tot;
		if(ql==l&&r==qr){flg[i]=1;return;}
		LL mid=(l+r)>>1;
		if(ql<mid)insert(ch[i][0],l,mid,ql,qr<mid?qr:mid);
		if(qr>mid)insert(ch[i][1],mid,r,ql>mid?ql:mid,qr);
	}
}A,B;
vector<pair<int,int> >q[LOG+5];
LL BFS(int d,LL sum)
{
	for(int i=0;i<(int)q[d].size();i++)
        if(A.flg[q[d][i].first]||B.flg[q[d][i].second])
			return (sum+1ll*((1ll<<d)-1)%mod*inv2)%mod*((1ll<<d)%mod)%mod;
    LL ret=0;
    for(int x=0;x<=1;x++){
        q[d-1].clear();
        for(int i=0;i<(int)q[d].size();i++)
            for(int y=0;y<=1;y++)
                if (A.ch[q[d][i].first][y]&&B.ch[q[d][i].second][x^y])
                    q[d-1].push_back(make_pair(A.ch[q[d][i].first][y],B.ch[q[d][i].second][x^y]));
        if(q[d-1].size())ret=(ret+1ll*BFS(d-1,sum|(1ll*x<<(d-1))))%mod;
    }
    return ret;
}
int main()
{
	int na,nb,i;LL l,r;
	na=gi();
	for(i=1;i<=na;i++){
		l=gi();r=gi()+1;
		A.insert(A.rt,0,1ll<<LOG,l,r);
	}
	nb=gi();
	for(i=1;i<=nb;i++){
		l=gi();r=gi()+1;
		B.insert(B.rt,0,1ll<<LOG,l,r);
	}
	q[LOG].push_back(make_pair(A.rt,B.rt));
	printf("%lld\n",BFS(LOG,0ll));
}

 

 

 

[HAOI2017]八縱八橫

題意: 給一個圖,帶加邊,刪邊,保證圖聯通,求從1號點出發異或值最大的迴路

題解:線段樹分治+bitset線性基+帶權並查集

板題,沒什麼好說的

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<vector>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 1003
#define BS bitset<N>
struct LBS{
	BS bas[N];
	void insert(BS x){
		for(int i=1000;i>=0;i--)if(x[i]){
			if(bas[i].none()){bas[i]=x;return;}
			x=x^bas[i];
		}
	}
	BS getmax(){
		BS ret;
		for(int i=1000;i>=0;i--)if(!ret[i])
			if(!bas[i].none())ret=ret^bas[i];
		return ret;
	}
}empt;
struct edge{
	int u,v;BS w;
}tmp,E[N];
#define lc i<<1
#define rc i<<1|1
struct node{
	int l,r;
	vector<edge> x;
}a[N<<2];
void build(int i,int l,int r)
{
	a[i].l=l;a[i].r=r;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(lc,l,mid);build(rc,mid+1,r);
}
void insert(int i,int l,int r)
{
	if(a[i].l>r||a[i].r<l)return;
	if(l<=a[i].l&&a[i].r<=r){
		a[i].x.push_back(tmp);
		return;
	}
	insert(lc,l,r);insert(rc,l,r);
}
int pos[N];
char ch[N],tt[10];
BS gw()
{
	scanf("%s",ch);BS w;int len=strlen(ch);
	for(int j=0;j<len;j++)if(ch[j]=='1')w.set(len-j-1);
	return w;
}
void print(BS x)
{
	int i;
	for(i=1000;i>=0;i--)if(x[i])break;
	if(i>=0)for(;i>=0;i--)if(x[i])printf("1");else printf("0");
	printf("\n");
}
int fa[N],H[N];BS dis[N],zer;
int stk[10*N],top;
int find(int x){while(x!=fa[x])x=fa[x];return x;}
BS getdis(int x){BS ret;while(x!=fa[x])ret=ret^dis[x],x=fa[x];return ret;}
void back(int tmp)
{
	while(top>tmp){
		if(stk[top]<0)H[-stk[top]]--;
		else dis[stk[top]]=zer,fa[stk[top]]=stk[top];
		top--;
	}
}
void solve(int i,LBS now)
{
	int tmptop=top;
	for(int j=0;j<(int)a[i].x.size();j++){
		int u=a[i].x[j].u,v=a[i].x[j].v;
		int p=find(u),q=find(v);
		BS w=a[i].x[j].w^getdis(u)^getdis(v);
		if(p==q)now.insert(w);
		else{
			if(H[p]<H[q])swap(p,q);
			fa[q]=p;dis[q]=w;stk[++top]=q;
			if(H[p]==H[q])H[p]++,stk[++top]=-p;
		}
	}
	if(a[i].l==a[i].r){
		print(now.getmax());
		back(tmptop);
		return;
	}
	solve(lc,now);solve(rc,now);
	back(tmptop);
}
int main()
{
	int n,m,Q,i,u;BS w;
	n=gi();m=gi();Q=gi();
	build(1,0,Q);
	for(i=1;i<=m;i++){
		tmp.u=gi();tmp.v=gi();tmp.w=gw();
		insert(1,0,Q);
	}
	int K=0;
	for(i=1;i<=Q;i++){
		scanf("%s",tt);
		if(tt[0]=='A'){
			tmp.u=gi();tmp.v=gi();tmp.w=gw();
			pos[++K]=i;E[K]=tmp;
		}
		else{
			if(tt[1]=='h'){
				u=gi();tmp=E[u];
				insert(1,pos[u],i-1);
				//printf("%d %d:%d %d ",pos[u],i-1,tmp.u,tmp.v);
				//print(tmp.w);
				E[u].w=gw();pos[u]=i;
			}
			else{
				u=gi();tmp=E[u];
				insert(1,pos[u],i-1);
				//printf("%d %d:%d %d ",pos[u],i-1,tmp.u,tmp.v);
				//print(tmp.w);
				pos[u]=0;
			}
		}
	}
	for(i=1;i<=K;i++)if(pos[i])
		tmp=E[i],insert(1,pos[i],Q);
	for(i=1;i<=n;i++)fa[i]=i,H[i]=1;
	solve(1,empt);
}

 

 

 

Bipartite Checking

題意:一個圖,帶加邊刪邊,判斷此圖是否爲二分圖

題解:線段樹分治+帶權並查集

板題

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 100005
#define lc i<<1
#define rc i<<1|1
map<pair<int,int>,int>mp;
map<pair<int,int>,int>::iterator it;
struct node{
	int l,r;
	vector<pair<int,int> >x;
}a[N<<2];
void build(int i,int l,int r)
{
	a[i].l=l;a[i].r=r;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(lc,l,mid);build(rc,mid+1,r);
}
void insert(int i,int l,int r,pair<int,int> k)
{
	if(a[i].l>r||a[i].r<l)return;
	if(l<=a[i].l&&a[i].r<=r){
		a[i].x.push_back(k);
		return;
	}
	insert(lc,l,r,k);insert(rc,l,r,k);
}
bool ans[N];
int fa[N],H[N],dis[N];
int stk[N],top;
int find(int x){while(x!=fa[x])x=fa[x];return x;}
bool getdis(int x){bool ret=0;while(x!=fa[x])ret^=dis[x],x=fa[x];return ret;}
void back(int tmptop)
{
	while(top>tmptop){
		if(stk[top]<0)H[-stk[top]]--;
		else fa[stk[top]]=stk[top],dis[stk[top]]=0;
		top--;
	}
}
void solve(int i,bool flg)
{
	int tmptop=top;
	for(int j=0;j<(int)a[i].x.size();j++){
		int u=a[i].x[j].first,v=a[i].x[j].second;
		int p=find(u),q=find(v);
		bool w=getdis(u)^getdis(v)^1;
		if(p==q)flg|=w;
		else{
			if(H[p]<H[q])swap(p,q);
			fa[q]=p;dis[q]=w;stk[++top]=q;
			if(H[p]==H[q])stk[++top]=-p;
		}
	}
	if(a[i].l==a[i].r){
		ans[a[i].l]=flg;
		back(tmptop);
		return;
	}
	solve(lc,flg);
	solve(rc,flg);
	back(tmptop);
}
int main()
{
	int n,Q,i,u,v;
	n=gi();Q=gi();
	build(1,1,Q);
	for(i=1;i<=Q;i++){
		u=gi();v=gi();if(u>v)swap(u,v);
		pair<int,int> tmp=make_pair(u,v);
		int &pp=mp[tmp];
		if(pp==0)pp=i;
		else{
			insert(1,pp,i-1,tmp);
			//printf("%d %d:%d %d\n",pp,i-1,tmp.first,tmp.second);
			pp=0;
		}
	}
	for(it=mp.begin();it!=mp.end();it++)
		if(it->second){
			insert(1,it->second,Q,it->first);
			//printf("%d %d:%d %d\n",it->second,Q,(*it).first.first,(*it).first.second);
		}
	for(i=1;i<=n;i++)fa[i]=i,H[i]=1;
	solve(1,0);
	for(i=1;i<=Q;i++){
		if(!ans[i])printf("YES\n");
		else printf("NO\n");
	}
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章