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

转载于:https://www.cnblogs.com/BlogOfchc1234567890/p/11284731.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值