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");
	}
}

 

 

 

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