$$ \newcommand{\seq}[2]{{#1}_{1},{#1}_{2},\cdots,{#1}_{#2}} \newcommand{\num}[1]{1,2,\cdots,#1} \newcommand{\stra}[2]{\begin{bmatrix}#1 \\ #2\end{bmatrix}} \newcommand{\strb}[2]{\begin{Bmatrix}#1 \\ #2\end{Bmatrix}} \newcommand{\dw}[1]{\underline{#1}} \newcommand{\up}[1]{\overline{#1}} $$

Codeforces 1198 & 1199

1198 D

你需要维护一个序列,支持两种操作:

  • 对于 \(1\le i\le n\)\(a[i] \leftarrow \max(a[i],x)\)
  • 对于给定的 \(p\)\(a[p] \leftarrow x\)

\((1\le n,Q\le 2*10^5)\)

Examples

input
4
1 2 3 4
3
2 3
1 2 2
2 1
output
3 2 3 4
input
5
3 50 2 1 10
3
1 2 0
2 8
1 3 20
output
8 8 20 8 10

线段树。

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=200003;
struct node{
	int val,z;
}t[maxn<<2];
int n,Q;
void pushdown(int p,int l,int r){
	if(l==r){t[p].val=max(t[p].val,t[p].z),t[p].z=0;return;}
	t[p<<1].z=max(t[p<<1].z,t[p].z);
	t[p<<1|1].z=max(t[p<<1|1].z,t[p].z);
	t[p].z=0;
}
void build(int p,int l,int r){
	if(l==r){
		scanf("%d",&t[p].val);
		return;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
}
void change(int p,int l,int r,int pos,int k){
	pushdown(p,l,r);
	if(l==r){
		t[p].val=k;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)change(p<<1,l,mid,pos,k);
	else change(p<<1|1,mid+1,r,pos,k);
}
int query(int p,int l,int r,int pos){
	pushdown(p,l,r);
	if(l==r)return t[p].val;
	int mid=(l+r)>>1;
	if(pos<=mid)return query(p<<1,l,mid,pos);
	else return query(p<<1|1,mid+1,r,pos);
}
int main(){
	scanf("%d",&n);
	build(1,1,n);
	scanf("%d",&Q);
	int mo,x,y;
	while(Q--){
		scanf("%d%d",&mo,&x);
		if(mo==1){
			scanf("%d",&y);
			change(1,1,n,x,y);
		}
		else{
			pushdown(1,1,n);
			t[1].z=x;
		}
	}
	for(int i=1;i<=n;i++)printf("%d ",query(1,1,n,i));
	return 0;
}

1199 E

给你一个 \(3*n\) 个节点 \(m\) 条边的无向图,如果存在一个匹配大小为 \(n\) 输出这个匹配;否则如果存在一个独立集大小为 \(n\) 输出这个独立集;否则输出Impossible。\((n\le 10^5,m\le 5*10^5)\)

Example

input
4
1 2
1 3
1 2
1 2
1 3
1 2
2 5
1 2
3 1
1 4
5 1
1 6
2 15
1 2
1 3
1 4
1 5
1 6
2 3
2 4
2 5
2 6
3 4
3 5
3 6
4 5
4 6
5 6
output
Matching
2
IndSet
1
IndSet
2 4
Matching
1 15

先找出任意一个匹配,如果匹配大小 \(\geq n\) ,输出;否则找出所有不在这个匹配中的节点,这些节点构成的集合一定是原图的一个独立集,输出。不存在Impossible的情况。
证明:如果匹配大小 \(<n\) ,那么剩下的节点数量肯定 \(\geq n\)

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=500003;
bool vis[maxn];
int n,m,ANS[maxn];
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=3*n;i++)vis[i]=0;
		int CNT=0;
		for(int i=1;i<=m;i++){
			int u,v;
			scanf("%d%d",&u,&v);
			if(!vis[u]&&!vis[v]){
				vis[u]=vis[v]=1;
				ANS[++CNT]=i;
			}
		}
		if(CNT>=n){
			puts("Matching");
			for(int i=1;i<=n;i++)printf("%d%c",ANS[i],i<n?' ':'\n');
		}
		else{
			puts("IndSet");
			CNT=0;
			for(int i=1;i<=3*n;i++)if(!vis[i])ANS[++CNT]=i;
			assert(CNT>=n);
			for(int i=1;i<=n;i++)printf("%d%c",ANS[i],i<n?' ':'\n');
		}
	}
	return 0;
}

1199 F

有一个 \(n*n\) 的矩阵,有些位置是黑的,有些是白的,现在你有若干次染色机会,每次你可以选择一个高为 \(h\) 宽为 \(w\) 的长方形,把其中的元素全染成白色,代价为 \(\max(h,w)\) 。求最小代价使得能把整个矩阵染成白色。 \((n\le 50)\)

Examples

input

3
###
#.#
###

output

3

input

3
...
...
...

output

0

input

4
#...
....
....
#...

output

2

input

5
#...#
.#.#.
.....
.#...
#....

output

5

\(O(n^5)\;\text{dp}\)

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=53;
int n,sum[maxn][maxn],dp[maxn][maxn][maxn][maxn];
char s[maxn][maxn];
int val(int ii,int jj,int i,int j){
	return sum[i][j]-sum[ii-1][j]-sum[i][jj-1]+sum[ii-1][jj-1]?max(i-ii+1,j-jj+1):0;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",s[i]+1);
		for(int j=1;j<=n;j++){
			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(s[i][j]=='#');
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int ii=i;ii>=1;ii--){
				for(int jj=j;jj>=1;jj--){
					dp[ii][jj][i][j]=val(ii,jj,i,j);
					for(int k=ii;k<i;k++){
						dp[ii][jj][i][j]=min(dp[ii][jj][i][j],dp[ii][jj][k][j]+dp[k+1][jj][i][j]);
					}
					for(int k=jj;k<j;k++){
						dp[ii][jj][i][j]=min(dp[ii][jj][i][j],dp[ii][jj][i][k]+dp[ii][k+1][i][j]);
					}
				}
			}
		}
	}
	printf("%d\n",dp[1][1][n][n]);
	return 0;
}

1198 E

字符矩阵形式输入变成两个坐标输入,两个坐标之间的所有格子都是黑的;代价变为 \(\min(h,w)\)\(n\le 10^9\) ;其它同1199 F。

Examples

inputCopy
10 2
4 1 5 10
1 4 10 5
outputCopy
4
inputCopy
7 6
2 1 2 1
4 2 4 3
2 5 2 5
2 3 5 3
1 2 1 2
3 2 5 3
outputCopy
3

本题做法和上题风马牛不相及。
首先肯定是一行一行或一列一列删最优。
看到算法标签有网络流和二分图匹配,于是想到经典的二分图最小点覆盖。
于是,先把所有区间的左端点值和右端点值+1离散化,设离散化数组为 \(mp[i]\) ,然后建二分图,每个节点 \(i\) 代表区间 \([mp[i],mp[i+1])\) ,然后对于每个输入中的矩形,把对应的节点连边。本题不是普通的二分图,所以推荐使用dinic跑。

Code

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=40003,maxm=400003,INF=1050000000;
struct edge{int to,next,w;}e[maxm<<1];
int head[maxn],head1[maxn],cnte;
void add(int u,int v,int w){e[++cnte].to=v,e[cnte].w=w,e[cnte].next=head[u],head[u]=cnte;}
void addedge(int u,int v,int w){add(u,v,w),add(v,u,0);}
int n,s,t,dep[maxn],q[maxn];
bool bfs(){
	for(int i=1;i<=n;i++)dep[i]=0,head1[i]=head[i];
	dep[s]=1;
	int *qhead=q,*qtail=q;
	*qtail++=s;
	while(qhead!=qtail){
		int u=*qhead++;
		for(int i=head[u];~i;i=e[i].next){
			int v=e[i].to;
			if(e[i].w&&dep[v]==0){
				dep[v]=dep[u]+1;
				*qtail++=v;
			}
		}
	}
	return dep[t]!=0;
}
int dfs(int u,int low){
	if(low==0||u==t)return low;
	int flow=0;
	for(int &i=head1[u];~i;i=e[i].next){
		int v=e[i].to;
		if(e[i].w&&dep[v]==dep[u]+1){
			int tmp=dfs(v,min(low,e[i].w));
			if(tmp==0)dep[v]=0;
			else{
				flow+=tmp;
				low-=tmp;
				e[i].w-=tmp;
				e[i^1].w+=tmp;
				if(low==0)break;
			}
		}
	}
	return flow;
}
int dinic(){
	int ans=0;
	while(bfs())ans+=dfs(s,INF);
	return ans;
}
int N,M,x1[maxn],x2[maxn],y1[maxn],y2[maxn],mp[maxn],cntmp;
int main(){
	scanf("%d%d",&N,&M);
	for(int i=1;i<=M;i++){
		scanf("%d%d%d%d",x1+i,y1+i,x2+i,y2+i);
		x2[i]++,y2[i]++;
		mp[++cntmp]=x1[i],mp[++cntmp]=y1[i],mp[++cntmp]=x2[i],mp[++cntmp]=y2[i];
	}
	sort(mp+1,mp+cntmp+1);
	cntmp=unique(mp+1,mp+cntmp+1)-mp-1;
	s=cntmp*2+1,n=t=s+1;
	for(int i=1;i<=n;i++)head[i]=-1;
	cnte=-1;
	for(int i=1;i<=M;i++){
		x1[i]=lower_bound(mp+1,mp+cntmp+1,x1[i])-mp;
		y1[i]=lower_bound(mp+1,mp+cntmp+1,y1[i])-mp;
		x2[i]=lower_bound(mp+1,mp+cntmp+1,x2[i])-mp;
		y2[i]=lower_bound(mp+1,mp+cntmp+1,y2[i])-mp;
		for(int j=x1[i];j<x2[i];j++){
			for(int k=y1[i];k<y2[i];k++){
				addedge(j,k+cntmp,INF);
			}
		}
	}
	for(int i=1;i<cntmp;i++){
		addedge(s,i,mp[i+1]-mp[i]);
		addedge(i+cntmp,t,mp[i+1]-mp[i]);
	}
	printf("%d\n",dinic());
	return 0;
}

1198 F

有一个数组,现在你要把它分成两部分,要求每部分的\(\gcd\)值为1。不可能输出NO。 \((n\le 10^5)\)

Examples

input
4
2 3 6 7
output
YES
2 2 1 1
input
5
6 15 35 77 22
output
YES
2 1 2 1 1
input
5
6 10 15 1000 75
output
NO

看代码,你的嘴巴会张大

Code

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
const int maxn=100003;
int n,b[maxn];
P a[maxn];
int main(){
	srand(19260817);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i].first),a[i].second=i;
	while(double(clock())/CLOCKS_PER_SEC<0.47){
		random_shuffle(a+1,a+n+1);
		int g1=0,g2=0;
		for(int i=1;i<=n;i++){
			if(rand()&1){
				if(!g1)g1=a[i].first,b[a[i].second]=1;
				else if(a[i].first%g1)g1=__gcd(g1,a[i].first),b[a[i].second]=1;
				else g2=__gcd(g2,a[i].first),b[a[i].second]=2;
			}
			else{
				if(!g2)g2=a[i].first,b[a[i].second]=2;
				else if(a[i].first%g2)g2=__gcd(g2,a[i].first),b[a[i].second]=2;
				else g1=__gcd(g1,a[i].first),b[a[i].second]=1;
			}
		}
		if(g1==1&&g2==1){
			puts("YES");
			for(int i=1;i<=n;i++)printf("%d ",b[i]);
			return 0;
		}
	}
	puts("NO");
	return 0;
}
posted @ 2019-08-01 19:20  chc_1234567890  阅读(260)  评论(0编辑  收藏  举报