9.4联合作战战果

1.处理内容

二分图相关 2题

树剖 2题

Floyd 1题

2-SAT 1题

最小生成树 1题

tarjan 1题

点分治 1题

模拟 1题

2.二分图

首先是比较简单的二分图判定

(1)双栈排序(NOIP2008提高组) 

题面见链接https://www.luogu.org/problem/show?pid=1155

分析:考虑在什么情况下两数不能同栈

在通过各种手段后我们可以推出:

当a[i]

所以根据这个原则n^2建边即可,判一圈之后O(n)模拟即可

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(long long x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(48+buf[buf[0]--]);
	return ;
}
int tot,nxt[111111],goal[111111],first[111111],co[1111],a[1111],mini[1111],n,stack1[1111],stack2[1111],top1,top2,s,top;
bool tag;
void addedge(int a,int b){
	++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;
	++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;
	return ;
}
void dfs(int tmpc,int pos){
	if(co[pos]!=0){
		if(co[pos]!=tmpc)
			tag=true;
		return ;
	}
		
	if(tag) return;
	co[pos]=tmpc;
	for(int p=first[pos];p;p=nxt[p])
		dfs(3-tmpc,goal[p]);
	return ;
}
signed main(){
	n=read();
	for(int i=1;i<=n;++i)
		a[i]=read();
	mini[n]=a[n];
	for(int i=n-1;i;--i)
		mini[i]=min(mini[i+1],a[i]);
	for(int i=1;i<=n;++i)
		for(int j=i+1;jmini[j+1])
				addedge(i,j);
	for(int i=1;i<=n;++i)
		if(!co[i]) dfs(1,i);
	if(tag){
		putchar('0');
		return 0;
	}
	s=1;top=1;
	for(int i=1;i<=n*2;++i){
		if(s<=n){
			if(stack1[top1]==top){
				--top1;
				putchar('b');putchar(' ');
				++top;
			}else if(co[s]==1){
				stack1[++top1]=a[s++];
				putchar('a');putchar(' ');
			}else if(stack2[top2]==top){
				--top2;
				putchar('d');putchar(' ');
				++top;
			}else{
				stack2[++top2]=a[s++];
				putchar('c');putchar(' ');
			}
		}else{
			if(stack1[top1]==top){
				--top1;
				putchar('b');putchar(' ');
				++top;
			}else{
				--top2;
				putchar('d');putchar(' ');
				++top;
			}
		}
	}
	return 0;
}
然后是同样简单的二分图匹配
(2)矩阵游戏(ZJOI2007)

题面依然见链接http://www.lydsy.com/JudgeOnline/problem.php?id=1059

分析:很显然,如果两点在同行或同列,那么无论如何变换其依然为同行或同列

我们现在只需要求是否有n点异行异列即可

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(long long x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(48+buf[buf[0]--]);
	return ;
}
int n,used[233],ans,con[233],T,map[233][233];
bool check(int x){
	for(int i=1;i<=n;++i)
		if(map[x][i]&&!used[i]){
			used[i]=true;
			if(con[i]==0||check(con[i])){
				con[i]=x;
				return true;
			}
		}
	return false ;
}
void hungary(){
	for(int i=1;i<=n;++i){
		memset(used,0,sizeof(used));
		if(check(i)) ++ans; 
	}
}
signed main(){
	T=read();
	while(T--){
		memset(used,0,sizeof(used));
		memset(con,0,sizeof(con));
		ans=0;
		n=read();
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				map[i][j]=read();
		hungary();
		if(ans==n) puts("Yes");
		else puts("No");
	}
}
3.树剖系列

(1)水树剖求LCA

又一次贴代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline long long read(){
	long long i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(long long x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(48+buf[buf[0]--]);
	return ;
}
#define stan 55555
int tot,nxt[stan*2],first[stan],goal[stan*2],dis[stan*2];
int sze[stan],to[stan],fa[stan],dep[stan],son[stan],top[stan];
int n,m,a,b,c;
void addedge(int a,int b,int c){
	++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;dis[tot]=c;
	++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;dis[tot]=c;
	return ;	
}
void preact(){
	static int que[111111];
	int q=1;
	que[q]=0;dep[0]=1;
	for(int i=1;i<=n;++i){
		int u=que[i];sze[u]=1;
		for(int p=first[u];p;p=nxt[p])
			if(goal[p]!=fa[u]){
				fa[goal[p]]=u;
				dep[goal[p]]=dep[u]+1;
				to[goal[p]]=to[u]+dis[p];
				que[++q]=goal[p];
			}
	}
	for(int i=n;i>=2;--i){
		int u=que[i];
		sze[fa[u]]+=sze[u];
		if(sze[u]>sze[son[fa[u]]])
			son[fa[u]]=u;
	}
	for(int i=1;i<=n;++i){
		int u=que[i];
		if(top[u]) continue;
		for(int v=u;v;v=son[v])
			top[v]=u;
	}
	return ;
}
int solve(int u,int v){
	int x=u,y=v;
	while(top[u]!=top[v]){
		if(dep[top[u]]dep[v]) swap(u,v);
	return to[x]+to[y]-to[u]*2;
}
signed main(){
	n=read();
	for(int i=1;i
(2)染色(SDOI2011)

题面其实很短,但我就是想甩链接http://www.lydsy.com/JudgeOnline/problem.php?id=2243

分析:其实是很裸的树链剖分,但是线段树合并时要注意

   那么具体该注意些什么呢?

  1.线段树标记下放后返回时要更改

	lef[pos]=lef[pos<<1];righ[pos]=righ[pos<<1|1];
  2.修改后合并时如果左右颜色相同则计数减一

	if(lef[pos<<1|1]^righ[pos<<1]) cont[pos]=cont[pos<<1]+cont[pos<<1|1];
	else cont[pos]=cont[pos<<1]+cont[pos<<1|1]-1;
  3.查询时两个子区间如果左右颜色相同则返回值减一,但要确保能两区间都会有贡献
	if(x<=mid&&mid
  4.最后计数时要抛去算过一次的LCA

	return pathsum(a,u)+pathsum(b,u)-1;

所以代码就调出来了

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline long long read(){
	long long i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(long long x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(48+buf[buf[0]--]);
	return ;
}
#define stan 111111
int tot,nxt[stan*2],first[stan],goal[stan*2],dis[stan*2];
int sze[stan],to[stan],fa[stan],dep[stan],son[stan],top[stan],idx[stan],pos[stan],now;
int cont[stan*4],tag[stan*4],lef[stan*4],righ[stan*4];
int n,m,a,b,c,co[stan];
char order[10];
void addedge(int a,int b){
	++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;
	++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;
	return ;	
}
void preact(){
	static int que[111111];
	int q=1;
	que[q]=1;dep[1]=1;
	for(int i=1;i<=n;++i){
		int u=que[i];sze[u]=1;
		for(int p=first[u];p;p=nxt[p])
			if(goal[p]!=fa[u]){
				fa[goal[p]]=u;
				dep[goal[p]]=dep[u]+1;
				que[++q]=goal[p];
			}
	}
	for(int i=n;i>=2;--i){
		int u=que[i];
		sze[fa[u]]+=sze[u];
		if(sze[u]>sze[son[fa[u]]])
			son[fa[u]]=u;
	}
	for(int i=1;i<=n;++i){
		int u=que[i];
		if(top[u]) continue;
		for(int v=u;v;v=son[v]){
			pos[v]=++now;
			idx[now]=v; 
			top[v]=u;
		}
	}
	return ;
}
void build(int x,int l,int r){
	cont[x]=1;tag[x]=-1;
	if(l==r) return ;
	int mid=l+r>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	return ;
}
void pushdown(int pos,int l,int r){
	int tmp=tag[pos];tag[pos]=-1;
	if(tmp==-1||l==r) return ;
	cont[pos<<1]=cont[pos<<1|1]=1;
	tag[pos<<1]=tag[pos<<1|1]=tmp;
	lef[pos<<1]=lef[pos<<1|1]=tmp;
	righ[pos<<1]=righ[pos<<1|1]=tmp;
	return ;
}
void raiseup(int pos){
	lef[pos]=lef[pos<<1];righ[pos]=righ[pos<<1|1];
	if(lef[pos<<1|1]^righ[pos<<1]) cont[pos]=cont[pos<<1]+cont[pos<<1|1];
	else cont[pos]=cont[pos<<1]+cont[pos<<1|1]-1;
	return ;
}
void change(int pos,int l,int r,int x,int y,int d){
	pushdown(pos,l,r);
	if(x<=l&&r<=y){
		lef[pos]=righ[pos]=d;
		cont[pos]=1;tag[pos]=d;
		return ;
	}
	int mid=l+r>>1;
	if(x<=mid) change(pos<<1,l,mid,x,y,d);
	if(y>mid) change(pos<<1|1,mid+1,r,x,y,d);
	raiseup(pos);
}
int querysum(int pos,int l,int r,int x,int y){
	pushdown(pos,l,r);
	int ret=0;
	if(x<=l&&r<=y) return cont[pos];
	int mid=l+r>>1;
	if(mid>=x) ret+=querysum(pos<<1,l,mid,x,y);
	if(mid>1;
	if(p<=mid) return check(pos<<1,l,mid,p);
	else return check(pos<<1|1,mid+1,r,p);
}
int pathsum(int u,int v){
	int ret=0;
	while(top[u]!=top[v]){
		ret+=querysum(1,1,n,pos[top[u]],pos[u]);
		if(check(1,1,n,pos[top[u]])==check(1,1,n,pos[fa[top[u]]])) --ret;
		u=fa[top[u]];
	}
	ret+=querysum(1,1,n,pos[v],pos[u]);
	return ret;
}
void pathchange(int u,int v,int data){
	while(top[u]!=top[v]){
		change(1,1,n,pos[top[u]],pos[u],data);
		u=fa[top[u]];
	}
	change(1,1,n,pos[v],pos[u],data);
}
int lca(int u,int v){
	int x=u,y=v;
	while(top[u]!=top[v]){
		if(dep[top[u]]dep[v]) swap(u,v);
	return u;
}
int solve(int a,int b){
	int u=lca(a,b);
	return pathsum(a,u)+pathsum(b,u)-1;
}
signed main(){
	n=read();m=read();
	for(int i=1;i<=n;++i)
		co[i]=read();
	for(int i=1;i
4.Floyd

5.2-SAT

奶牛议会(USACO2011.Jan)

甩链接才是最方便的...貌似是权限题
题面:n点m个限制,求对于一种合法方案每个点的取舍情况

分析:对于每个要求拆开正常建边即可,最后枚举每个点的取舍,判断一下即可

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(long long x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(48+buf[buf[0]--]);
	return ;
}
int tot,nxt[111111],goal[111111],first[4444],n,m,a,b,c,d,p,q;
char order[2222];
bool mark[2222];
void addedge(int a,int b){
	++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;
	return ;
}
void dfs(int pos){
	mark[pos]=1;
	for(int p=first[pos];p;p=nxt[p])
		if(!mark[goal[p]])
			dfs(goal[p]);
	return ;
}
bool check(int pos){
	memset(mark,0,sizeof(mark));
	dfs(pos);
	for(int i=1;i<=n;++i)
		if(mark[2*i]&&mark[2*i-1])return 0;
	return 1;
}
signed main(){
	n=read();m=read();
	for(int i=1;i<=m;++i){
		a=read();
		scanf("%s",order);
		if(order[0]=='Y')
			a=a*2-1;
		else a<<=1;
		if(a&1) b=a+1;
		else b=a-1;
		c=read();
		scanf("%s",order);
		if(order[0]=='Y')
			c=c*2-1;
		else c<<=1;
		if(c&1) d=c+1;
		else d=c-1;
		addedge(b,c);addedge(d,a);
	}
	for(int i=1;i<=n;++i){
		p=check(i*2-1);
		q=check(i<<1);
		if(!p&&!q){
			puts("IMPOSSIBLE");
			return 0;
		}else if(p&&q) order[i]='?';
		else if(!p) order[i]='N';
		else order[i]='Y';
	}
	for(int i=1;i<=n;++i)
		putchar(order[i]);
	return 0;
}

6.最小生成树

版题,略

贴个代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(long long x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0])putchar(buf[buf[0]--]+48);
	return ;
}
struct Edge{
	int a,b,l;
}edge[1111111];
int n,x,y,m,cnt,ans,fa[1111];
bool cmp(Edge x,Edge y){
	return x.l
7.tarjan

这次我们用tarjan求割点

分析:对于一个点,如果其子节点无法不经过该点而到达其父节点部分,则该点为割点

   很明显这是把它假装成一棵树,所以我们用了tarjan

   注意第一个遍历的节点如果只有一个儿子的情况

还是甩个链接吧https://www.luogu.org/problem/show?pid=3388

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(long long x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(48+buf[buf[0]--]);
	return ;
}
int tot,nxt[222222],first[111111],goal[222222],dfn[111111],low[111111],ts,cutting[111111],ans,n,m,u,v;
void addedge(int a,int b){
	++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;
	++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;
	return ;
}
void tarjan(int fa,int u){
	dfn[u]=low[u]=++ts;
	int son=0;
	for(int p=first[u];p;p=nxt[p])
	
		if(!dfn[goal[p]]){
			++son;
			tarjan(u,goal[p]);
			low[u]=min(low[u],low[goal[p]]);
			if(low[goal[p]]>=dfn[u]) cutting[u]=true;
		}else if(dfn[goal[p]]
8.点分治

嗯就是我很久以前写过的那个
代码还是那个鬼样子

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline long long read(){
	long long i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(long long x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(48+buf[buf[0]--]);
	return ;
}
int tot,n,k,nxt[22222],first[11111],goal[22222],dis[22222],cnt,a,b,c;
int sze[11111],maxsze[11111],step[11111],dist[11111],root,total;
bool vis[11111];
int nop,ans;
void addedge(int a,int b,int c){
	++tot;nxt[tot]=first[a];first[a]=tot;goal[tot]=b;dis[tot]=c;
	++tot;nxt[tot]=first[b];first[b]=tot;goal[tot]=a;dis[tot]=c;
	return ;	
}
void dfsroot(int pos,int fa){
	sze[pos]=1;maxsze[pos]=0;
	for(int p=first[pos];p;p=nxt[p])
		if(goal[p]!=fa&&!vis[goal[p]]){
			dfsroot(goal[p],pos);
			sze[pos]+=sze[goal[p]];
			if(sze[goal[p]]>maxsze[pos]) maxsze[pos]=sze[goal[p]];
		}
	if(total-sze[pos]>maxsze[pos]) maxsze[pos]=total-sze[pos];
	if(maxsze[root]>maxsze[pos]) root=pos;
	return ;
}
void topicaldfs(int pos,int fa){
	++cnt;
	step[cnt]=dist[pos];
	for(int p=first[pos];p;p=nxt[p])
		if(goal[p]!=fa&&!vis[goal[p]]){
			dist[goal[p]]=dist[pos]+dis[p];
			topicaldfs(goal[p],pos);
		}
	return ;
}
int calc(int pos,int dista){
	int ret=0;
	cnt=0,dist[pos]=dista;
	topicaldfs(pos,0);
	sort(step+1,step+cnt+1);
	for(int l=1,r=cnt;l

9.模拟

生日礼物(SCOI2009)

08-09年生日三部曲之一

题面在此http://www.lydsy.com/JudgeOnline/problem.php?id=1293

维护两组堆都能过...

一个为起点,然后k次枚举讫点,完了

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(long long x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(48+buf[buf[0]--]);
	return ;
}
priority_queue,greater >que[66];
int n,k,num[66],tmp1,tmp2,pos,ans;
bool tag;
signed main(){
	ans=2147483647;
	n=read();k=read();
	for(int i=1;i<=k;++i){
		num[i]=read();
		for(int j=1;j<=num[i];++j){
			pos=read();
			que[0].push(pos);
			que[i].push(pos);
		}
	}
	while(!que[0].empty()){
		tmp1=que[0].top();
		tmp2=0;
		for(int i=1;i<=k;++i){
			while(que[i].top()
局部完结撒花


你可能感兴趣的:(OI,最小生成树科,分治纲,线段树科,琐题集萃门,树链剖分科,图论纲)