Codeforces Round #357 (Div. 2)

本文解析了CodeForces竞赛中的两个题目,包括堆操作序列修正的最小化问题及基于父子关系的人际礼物交换序列设计问题,并提供了详细的算法思路与代码实现。

链接:http://codeforces.com/contest/681

problemA:水题

problemB:水题

problemC:给定一个堆的操作序列,操作有:insert x,getmin x,removemin。这个序列可能是非法的,求将这个序列补充成合法的并且添加的操作最少。

分析:按照给定的序列用优先队列模拟就行了,少了就加多了就删。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int MAX=1000000100;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const int INF=1000000010;
const double pi=acos(-1.0);
typedef double db;
typedef unsigned long long ull;
priority_queue<int, vector<int>, greater<int> >Q;
char s[20];
int a[N],b[N];
int main()
{
    int i,n,m=0;
    scanf("%d", &n);
    for (i=1;i<=n;i++) {
        scanf("%s", s);
        if (s[0]=='i') { a[i]=1;scanf("%d", &b[i]); }
        else if (s[0]=='g') { a[i]=2;scanf("%d", &b[i]); }
            else { a[i]=3;b[i]=0; }
    }
    for (i=1;i<=n;i++)
    if (a[i]==1) {
        m++;Q.push(b[i]);
    } else if (a[i]==2) {
            if (Q.empty()||Q.top()>b[i]) {
                Q.push(b[i]);m+=2;
            } else {
                while (!Q.empty()&&Q.top()<b[i]) { m++;Q.pop(); }
                if (!Q.empty()&&Q.top()==b[i]) m++;
                else { Q.push(b[i]);m+=2; }
            }
        } else {
            if (Q.empty()) m+=2;
            else { m++;Q.pop(); }
        }
    printf("%d\n", m);
    while (!Q.empty()) Q.pop();
    for (i=1;i<=n;i++)
    if (a[i]==1) {
        printf("insert %d\n", b[i]);
        m++;Q.push(b[i]);
    } else if (a[i]==2) {
            if (Q.empty()||Q.top()>b[i]) {
                printf("insert %d\n", b[i]);
                printf("getMin %d\n", b[i]);
                Q.push(b[i]);m+=2;
            } else {
                while (!Q.empty()&&Q.top()<b[i]) {
                    printf("removeMin\n");
                    m++;Q.pop();
                }
                if (!Q.empty()&&Q.top()==b[i]) {
                    printf("getMin %d\n", b[i]);m++;
                } else {
                    Q.push(b[i]);
                    printf("insert %d\n", b[i]);
                    printf("getMin %d\n", b[i]);
                    m+=2;
                }
            }
        } else {
            if (Q.empty()) {
                printf("insert 0\n");
                printf("removeMin\n");
                m+=2;
            } else {
                printf("removeMin\n");
                m++;Q.pop();
            }
        }
    return 0;
}


problemD:给定n个人和m对父子关系,给定n个人想要送礼物的祖先a[i]。要求输出一个最短的序列满足:对于第i个人,a[i]一定是i的所有祖先中最先在序列里出现的祖先。

分析:我的做法是将所有人的愿望按a[i]的深度从大到小排列,然后每次先插入深度更深的点到序列尾部,这是显然的。然后我们用dfs序+树状数组判断一下当前这个点的祖先中是否已经有非a[i]的点在序列中出现了。O(nlogn)。看了卿神的思路,才恍然大悟。这个题其实限制条件有点过于充足,限制得太紧。对于所有父子i-j,a[j]一定为j或者a[i],否则一定无解,因为这样的话i和j要送礼物的祖先的顺序是无解的。那么这题就只要dfs一下就行了。O(n)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int MAX=1000000100;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const int INF=1000000010;
const double pi=acos(-1.0);
typedef double db;
typedef unsigned long long ull;
struct node{
    int x,y;
}a[N];
int tot,d[N],u[N],v[N],pre[N];
void add(int x,int y) {
    v[tot]=y;pre[tot]=u[x];u[x]=tot++;
}
int n,f[N],q[N],de[N],in[N],out[N],ans[N];
void dfs(int x,int f) {
    de[x]=de[f]+1;
    in[x]=++tot;
    for (int i=u[x];i!=-1;i=pre[i]) dfs(v[i],x);
    out[x]=tot;
}
int cmd(node x,node y) {
    return de[x.y]>de[y.y];
}
void add_x(int x,int y) {
    for (;x<=n+1;x+=x&-x) f[x]+=y;
}
int getsum(int x) {
    int ret=0;
    for (;x;x-=x&-x) ret+=f[x];
    return ret;
}
int main()
{
    int i,m,x,y,bo,sum;
    scanf("%d%d", &n, &m);
    tot=0;memset(u,-1,sizeof(u));
    for (i=1;i<=m;i++) {
        scanf("%d%d", &x, &y);
        add(x,y);d[y]++;
    }
    for (i=1;i<=n;i++)
    if (d[i]==0) add(0,i);
    x=0;memset(q,0,sizeof(q));
    for (i=1;i<=n;i++) {
        a[i].x=i;scanf("%d", &a[i].y);
        if (!q[a[i].y]) { x++;q[a[i].y]=1; }
    }
    tot=0;dfs(0,0);
    sort(a+1,a+n+1,cmd);
    bo=1;y=0;
    memset(q,0,sizeof(q));
    memset(f,0,sizeof(f));
    for (i=1;i<=n;i++) {
        sum=getsum(in[a[i].x]);
        if (sum==0) {
            ans[++y]=a[i].y;q[a[i].y]=1;
            add_x(in[a[i].y],1);add_x(out[a[i].y]+1,-1);
        } else if (sum>1) bo=0;
            else if (!q[a[i].y]) bo=0;
    }
    if (!bo) printf("-1\n");
    else {
        printf("%d\n", x);
        for (i=1;i<=x;i++) printf("%d\n", ans[i]);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值