Codeforces Round #535 (Div. 3)

A. Two distinct points

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=10010;
const int inf=0x3f3f3f3f;
int n,m;
ll l1,r1,l2,r2;
int main(){
    cin>>n;
    while(n--)
    {
        cin>>l1>>r1>>l2>>r2;
        if(l1!=l2)
        cout<<l1<<" "<<l2<<endl;
        else cout<<l1<<" "<<r2<<endl;
    }
}
View Code

B. Divisors of Two Integers

题意:给出x,y两个数的所有因子,如果一个因子同时存在于xy中,那么这个因子在数列中会出现两次,让你还原xy。

首先最大的那个数肯定是x,然后统计每个数字出现次数,稍微想想就好了。

水。

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=10010;
const int inf=0x3f3f3f3f;
int n,m;
ll a[maxn];
int vis[10010];
int main(){
    cin>>n;
    ll maxx=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        maxx=max(a[i],maxx);
        vis[a[i]]++;
    }
    printf("%lld ",maxx);
    ll x=1;
    for(int i=1;i<=10000;i++)
    {
        if(maxx%i==0){
            vis[i]--;
        }
    }
    for(int i=1;i<=10000;i++)
    {
        if(vis[i])
        maxx=i;
    }
    printf("%lld
",maxx);
}
View Code

C. Nice Garland

题意:给出一个灯的排列,使同样的灯间隔为3,并且rgb三个颜色要循环,求改变灯的最少次数。

rgb总共有六种全排列,全列出来,暴力比较。

比赛时看错题目 wa两发  代码还写的丑 我是弟弟。

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=2*100010;
const int inf=0x3f3f3f3f;
int n,m;

char s[maxn];
string op="RGB";
int main(){
    cin>>n;
    cin>>s+1;
    int ans=inf;
    int tep=0;
    int f=0;
    op="RGB";
    for(int i=1;i<=n;i++)
    {
        if(s[i]!=op[i%3]){
            tep++;
        }
    }
    if(tep<ans){
        f=1;
        ans=tep;
    }
    op="BRG";
    tep=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]!=op[i%3]){
            tep++;
        }
    }
    if(tep<ans){
        f=2;
        ans=tep;
    }
    op="GBR";
    tep=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]!=op[i%3]){
            tep++;
        }
    }
    if(tep<ans){
        f=3;
        ans=tep;
    }
    
    op="RBG";
    tep=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]!=op[i%3]){
            tep++;
        }
    }
    if(tep<ans){
        f=4;
        ans=tep;
    }    
    
    op="BGR";
    tep=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]!=op[i%3]){
            tep++;
        }
    }
    if(tep<ans){
        f=5;
        ans=tep;
    }    
    
    op="GRB";
    tep=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]!=op[i%3]){
            tep++;
        }
    }
    if(tep<ans){
        f=6;
        ans=tep;
    }    
    

    printf("%d
",ans);
    if(f==2){
        for(int i=1;i<=n;i++)
        {
            if(i%3==1)printf("R");
            if(i%3==2)printf("G");
            if(i%3==0)printf("B");
        }
    }
    if(f==3){
        for(int i=1;i<=n;i++)
        {
            if(i%3==1)printf("B");
            if(i%3==2)printf("R");
            if(i%3==0)printf("G");
        }
    }
    if(f==4){
        for(int i=1;i<=n;i++)
        {
            if(i%3==1)printf("B");
            if(i%3==2)printf("G");
            if(i%3==0)printf("R");
        }
    }
    if(f==5){
        for(int i=1;i<=n;i++)
        {
            if(i%3==1)printf("G");
            if(i%3==2)printf("R");
            if(i%3==0)printf("B");
        }
    }
    
    if(f==6){
        for(int i=1;i<=n;i++)
        {
            if(i%3==1)printf("R");
            if(i%3==2)printf("B");
            if(i%3==0)printf("G");
        }
    }
    if(f==1){
        for(int i=1;i<=n;i++)
        {
            if(i%3==1)printf("G");
            if(i%3==2)printf("B");
            if(i%3==0)printf("R");
        }
    }
    puts("");
    
}
View Code

D. Diverse Garland

题意:还是给出一个灯的排列,要使相邻的灯颜色不一样,求改变灯的最少次数。

贪心的想,如果有两个相邻的灯,我肯定要改变其中一个,那么优先改变后面的肯定是最优的,因为我在改变第i+1个灯的时候,不但可以使其和 i 不一样,还能使其和i+2不一样。

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=2*100010;
const int inf=0x3f3f3f3f;
int n,m;

char s[maxn];
char ans[maxn];
string op="RGB";
int main(){
    cin>>n;
    cin>>s+1;
    s[n+1]='c';
    int tot=0;
    ans[1]=s[1];
    for(int i=2;i<=n;i++)
    {
        if(s[i]==ans[i-1]){
            for(int j=0;j<3;j++)
            {
                if(op[j]!=ans[i-1]&&op[j]!=s[i+1]){
                    ans[i]=op[j];
                    break;
                }
            }
            tot++;
        }else{
            ans[i]=s[i];
        }
    }
    printf("%d
",tot);
    for(int i=1;i<=n;i++)
    {
        printf("%c",ans[i]);
    }
    puts("");
}
View Code

 (E1) Array and Segments (Easy version)

题意:给出n个数字,这里n的范围是300,再给出m个区间,对于每个区间,你可以使下标在这个区间内所有数字都-1,也可以不操作。现在要求最优操作,使得操作过后最大值减最小值 最大,输出答案,并且输出选取了几个区间和哪些区间。

这道题和hard版本差的只是数据范围,有一个思想是通用的,就是假设 第 i 个数字是最后的最小值,那么使所有覆盖了这个 i 的所有区间都生效后,答案一定是 在选取了 i 后最优的。为什么呢?由于 i 是最小值,假设 j 是最大值,如果 j 没有被 i 连累 ,那么差值肯定会变大(i变小了,j没变),如果被 i 连累了,那么两个同时缩小,对答案没影响。所以我们只要枚举所有的 i 使 所有包含了 i 区间生效就可以了。

对于每一次枚举,在这个简单版本里,应该可以(没试过,时间复杂度是能结束的)暴力的把每一个区间都减去,然后比完最大值 - a[ i ]后,再把他还原。这样做的时间复杂度是o(n^3)。

我用了01差分。时间复杂度优化到o(n^2).

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=310;
const int inf=0x3f3f3f3f;
int n,m;
int a[maxn],b[maxn],l[maxn],r[maxn];
int c[maxn];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&l[i],&r[i]);
    }
    int ans=0,teo=1,tep=0;
    for(int i=1;i<=n;i++)
    {
        CLR(c,0);
        
        for(int j=1;j<=m;j++)
        {
            if(l[j]<=i&&r[j]>=i){
                c[l[j]]--;
                c[r[j]+1]++;
            }
        }
        int maxx=-inf;
        for(int j=1;j<=n;j++)
        {
            c[j]+=c[j-1];
            b[j]=a[j]+c[j];
            maxx=max(b[j],maxx);
        }
        if(maxx-b[i]>ans)
        {
            ans=maxx-b[i];
            teo=i;
        }
    }
    printf("%d
",ans);
    int tt[maxn];
    for(int i=1;i<=m;i++)
    {
        if(l[i]<=teo&&r[i]>=teo){
            tt[++tep]=i;
        }
    }
    printf("%d
",tep);
    for(int i=1;i<=tep;i++)
    {
        printf("%d",tt[i]);
        if(i<tep)printf(" ");
        else puts("");
    }
}
View Code

E2. Array and Segments (Hard version)

题意:和上题一样,n变成1e5.

上题的时间复杂度不能被接受了,所以我们要考虑优化。

看上一个版本,n的枚举显然是没法优化了,但是我们发现每次我们在对m个区间进行操作时浪费了很多的时间,所以我们在这里优化。

如果一个区间已经覆盖了[4,5],那么在我们处理完4这个点的信息后,显然不需要还原,因为这个区间对5还是有用的,如果我们把一个区间在 l 这个位置加入,在 r 这个位置消除,那我们对每一个区间就只需要处理两次,然后我们用线段树来维护a[ n ]数组的最大值,并且用线段树完成  -1 +1的操作,就可以了。

所以我们要先处理出这些区间是在哪个地方插入的,哪个地方删除的,所以还是用到差分的思想,每个 i 都开一个vector,然后对于标号为id 的区间 l r,往v[ l ]里塞入id, v[ r ]里塞入-id,正数代表我要加入某一个区间的影响,负数代表我要消除某一个区间的影响。

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=100010;
const int inf=0x3f3f3f3f;
ll a[maxn];
struct node{
    ll ma,lazy;
}tr[maxn<<2];
vector<int > v[maxn];
int l[maxn],r[maxn],n,m,tt[310];
ll ans,teo,tep;
void init(){
    ans=0,teo=0,tep=0;
    for(int i=1;i<=n;i++)
    {
        v[i].clear();
    }
}
void build(int o,int l,int r){
    if(l==r){
        tr[o].ma=a[l];
        tr[o].lazy=0;
        return ;
    }
    int mid=(l+r)>>1;
    build(o<<1,l,mid);
    build((o<<1)|1,mid+1,r);
    tr[o].ma=max(tr[o<<1].ma,tr[(o<<1)|1].ma);
    tr[o].lazy=0;
}
void pushup(int o){
    tr[o].ma=max(tr[o<<1].ma,tr[o<<1|1].ma);
}
void pushdown(int o,int l,int r){
    if(tr[o].lazy){
        tr[o<<1].lazy+=tr[o].lazy;
        tr[o<<1|1].lazy+=tr[o].lazy;
        tr[o<<1].ma+=tr[o].lazy;
        tr[o<<1|1].ma+=tr[o].lazy;
        tr[o].lazy=0;
    }
}
void update(int o,int l,int r,int ql,int qr,ll val){
    if(ql<=l&&r<=qr){
        tr[o].lazy+=val;
        tr[o].ma+=val;
        return;
    }
    pushdown(o,l,r);
    int mid=l+((r-l)>>1);
    if(ql<=mid)update(o<<1,l,mid,ql,qr,val);
    if(qr>=mid+1)update(o<<1|1,mid+1,r,ql,qr,val);
    pushup(o);
}
ll query(int o,int l,int r,int ql,int qr){
    if(ql<=l&&qr>=r)return tr[o].ma;
    pushdown(o,l,r);
    int mid=(l+r)>>1;
    ll ans=-inf;
    if(ql<=mid)ans=query(o<<1,l,mid,ql,qr);
    if(qr>=mid+1)ans=max(ans,query(o<<1|1,mid+1,r,ql,qr));
    return ans;
}
int main(){
    while(cin>>n>>m)
    {
        init();
        build(1,1,n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
        }
        build(1,1,n);
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&l[i],&r[i]);
            v[l[i]].push_back(i);
            v[r[i]+1].push_back(-i);
        }
        for(int i=1;i<=n;i++)
        {
            int sz=v[i].size();
            for(int j=0;j<sz;j++)
            {
                int x=v[i][j];
                if(x>=0){
                    update(1,1,n,l[x],r[x],-1);
                }else{
                    x=-x;
                    update(1,1,n,l[x],r[x],1);
                }
            }
            
            ll maxx=query(1,1,n,1,n);
            ll b=query(1,1,n,i,i);
            
            if(maxx-b>ans){
                ans=maxx-b;
                teo=i;
            }
        }
        //printf("debug
");
        for(int i=1;i<=m;i++)
        {
            if(l[i]<=teo&&r[i]>=teo){
                tt[++tep]=i;
            }
        }
        printf("%lld
%lld
",ans,tep);
        for(int i=1;i<=tep;i++)
        {
            printf("%d%c",tt[i],(i<tep)?' ':'
');
        }
    }
}
View Code

F. MST Unification

题意:给出一幅图,这幅图可能有很多最小生成树,现在你可以操作若干次,每次操作可以任选一条边使其权值+1,一条边可以多次选择,使得最小生成树唯一,并且最小生成树的权值不变。

首先克鲁斯卡尔跑一遍生成树,然后把有用边重新构图,变成一棵新的树,把所有无用边都标记一下,然后求边的两个端点和生成树形成的环的最大长度是不是等于自己,如果等于自己,则代表这条边可以取代这个最大长度,成为生成树的一部分。(环上不会有大于自己的边,否则就不会是最小生成树了)。求最大长度的方法是lca。

求lca的时候,dfs千万不要把1号节点的父节点设成-1,否则会re,oj可能还不报re只报wa,kk要是再这样写,kk就把题目吃下去。

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
struct edge{
    int u,v;
    ll w;
    friend bool operator <(const edge &a,const edge &b){
        return a.w<b.w;
    }
};
struct node{
    int to,Next;
    ll w;
}te[maxn<<1];
vector<edge>e;
int n,m,tot,dep[maxn];
int u,v,fa[maxn],head[maxn],f[maxn][30];
ll w,ma[maxn][30];
void addv(int u,int v,ll w){
    te[++tot].to=v;
    te[tot].w=w;
    te[tot].Next=head[u],head[u]=tot;
}
bool vis[maxn];
void init(){
    tot=0;
    CLR(head,-1);
    CLR(vis,0);
    e.clear();
    for(int i=1;i<=n;i++)fa[i]=i;
}
int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
void dfs(int u,int fa,int deep,ll w){
    f[u][0]=fa,dep[u]=deep,ma[u][0]=w;
    for(int i=head[u];i != -1;i=te[i].Next){
        int v=te[i].to;
        if(v==fa)continue;
        dfs(v,u,deep+1,te[i].w);
    }
}
void work(){
    for(int j=1;j<=20;j++)
    {
        for(int i=1;i<=n;i++)
        {
            f[i][j]=f[f[i][j-1]][j-1];
            ma[i][j]=max(ma[i][j-1],ma[f[i][j-1]][j-1]);
        }
    }
}
ll lca(int p,int q){
    if(dep[p]<dep[q])swap(p,q);
    ll res=0;
    for(int i=20;i>=0;i--)
    {
        if(dep[f[p][i]]>=dep[q])
        {
            res=max(res,ma[p][i]);
            p=f[p][i];
        }
    }
    if(p==q)return res;
    for(int i=20;i>=0;i--)
    {
        if(f[p][i]!=f[q][i]){
            res=max(res,ma[p][i]);
            res=max(res,ma[q][i]);
            p=f[p][i],q=f[q][i];
        }
    }
    
    return max(res,max(ma[p][0],ma[q][0]));
}
int main(){
    while(cin>>n>>m)
    {
        init();
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%lld",&u,&v,&w);
            e.push_back({u,v,w});
        }
        sort(e.begin(),e.end());
        for(int i=0;i<m;i++)
        {
            u=e[i].u,v=e[i].v;
            w=e[i].w;
            int fx=find(u),fy=find(v);
            if(fx!=fy){
                fa[fx]=fy;
                addv(u,v,w);
                addv(v,u,w);
            }else{
                vis[i]=1;
            }
        }
        
        dfs(1,0,1,0);
        work();
        int ans=0;
        for(int i=0;i<m;i++)
        {
            if(vis[i]){
                if(e[i].w==lca(e[i].u,e[i].v)){
                    ans++;
                }
            }
        }
        printf("%d
",ans);
    }
}
View Code
原文地址:https://www.cnblogs.com/mountaink/p/10315949.html