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
#include
#include
#include
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 S[N];
set::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>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

20200606分治算法总结_第1张图片

题解:动态开点线段Trie树(大概也只适用于这道题吧)

用BFS从高到低确定答案的某一位,同时在两棵线段Trie树上移动节点

当某一个节点被完全标记了,此时答案中低位的所有情况都可以被取到

那么这一部分的答案就是等差数列求和,首项就是已经确定了的高位的部分

复杂度应该是O(n*log10^18)

代码:

#include
#include
#include
#include
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(qlmid)insert(ch[i][1],mid,r,ql>mid?ql:mid,qr);
	}
}A,B;
vector >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<

 

 

 

[HAOI2017]八纵八横

题意: 给一个图,带加边,删边,保证图联通,求从1号点出发异或值最大的回路

题解:线段树分治+bitset线性基+带权并查集

板题,没什么好说的

代码:

#include
#include
#include
#include
#include
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
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 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=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]

 

 

 

Bipartite Checking

题意:一个图,带加边删边,判断此图是否为二分图

题解:线段树分治+带权并查集

板题

代码:

#include
#include
#include
#include
#include
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,int>mp;
map,int>::iterator it;
struct node{
	int l,r;
	vector >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 k)
{
	if(a[i].l>r||a[i].rtmptop){
		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]v)swap(u,v);
		pair 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");
	}
}

 

 

 

你可能感兴趣的:(图论,总结,分治)