• Codeforces 1198 & 1199


    1198 D

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

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

    ((1le n,Qle 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。((nle 10^5,mle 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?' ':'
    ');
    		}
    		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?' ':'
    ');
    		}
    	}
    	return 0;
    }
    

    1199 F

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

    Examples

    input

    3
    ###
    #.#
    ###
    

    output

    3
    

    input

    3
    ...
    ...
    ...
    

    output

    0
    

    input

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

    output

    2
    

    input

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

    output

    5
    

    (O(n^5); ext{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
    ",dp[1][1][n][n]);
    	return 0;
    }
    

    1198 E

    字符矩阵形式输入变成两个坐标输入,两个坐标之间的所有格子都是黑的;代价变为 (min(h,w))(nle 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
    ",dinic());
    	return 0;
    }
    

    1198 F

    有一个数组,现在你要把它分成两部分,要求每部分的(gcd)值为1。不可能输出NO。 ((nle 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;
    }
    
  • 相关阅读:
    Codeforces Round #650 (Div. 3)
    C. Count Triangles
    A National Pandemic (思维 + 树链剖分模版)
    扫描线专题
    扫描线模版
    莫队模版
    JS设计模式 -- 4种创建型模式
    滑动窗口的最大值 -- 单调队列
    JS链表实现栈和队列
    js数组扁平化的几种实现方式
  • 原文地址:https://www.cnblogs.com/BlogOfchc1234567890/p/11284731.html
Copyright © 2020-2023  润新知