Codeforces Round #457 (Div. 2)

比赛链接

这次还好unrated掉了qaq。。
否则我不是掉分掉到罗马尼亚去了= =

A. Jamie and Alarm Snooze

题意:

一个时刻表示为ab:cd。(24小时制)
有一个单位时间x,每次可以把时间向前推x分钟。
假如某个时刻的表示ab:cd中abcd有一个=7,就是幸运的。
问最少向前推几个x分钟,可以使时刻变成幸运的。

做法:

简单模拟。

易错点:

注意假如到了00:00这个时刻,向前推的时候要变成23:**。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std;

int main() {
    int x, a, b, aa, bb;
    scanf("%d", &x);
    scanf("%d%d", &a, &b); aa = a, bb = b;
    for(int i = 0; i <= 24*60; i ++) {
        int tmp = x*i; a = aa, b = bb;
        while(b < tmp) { b += 60; a --; if(a == -1) a = 23; }
        b -= tmp;
        if(b%10 == 7 || b/10 == 7 || a%10 == 7 || a/10 == 7) {
            printf("%d\n", i);
            return 0;
        }
    }
}

B. Jamie and Binary Sequence (changed after round)

题意:

给一个n和k,要你构造一个长为k的序列a1…ak,使得 i=1k2ai =n,并且从大到小排以后,先保证最大数最小,再保证字典序最大。

做法:

首先一个想法就是把n分解二进制,假如这个时候1的个数已经超过k了就是No,否则就把最大x的拆成两个x-1,直到长度=k了为止。
但是这样是不优的。因为无法保证字典序最大。

所以我们再稍微改进一下:
每次把所有cnt个最大值弹出,更改为2*cnt个次大的。
假如更改了以后长度超过k了,就不要改了,重新放进cnt个最大的,然后把最小的不停往下分。
否则如果没有超过k就改成次大的。
这样就能保证是最优的了。

易错点:

注意这个要用一个双端队列维护,如果手写双端队列l和r要从中间开始。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<queue>
using namespace std;
typedef long long ll;

ll n, k;
int d[100], q[1000010];

int main() {
    cin >> n >> k;
    int t = 0, len = 0;
    while(n) {
        if(n&1) d[++ len] = t;
        t ++; n >>= 1;
    }
    int l = 100001, r = 100000;
    for(int i = 1; i <= len; i ++) q[++ r] = d[i];
    if(len > k) { puts("No"); return 0; }
    bool flag = 0;
    while(r-l+1 < k) {
        if(flag) {
            int tmp = q[l ++];
            q[-- l] = tmp-1; q[-- l] = tmp-1;
            continue;
        }
        int tmp = q[r]; int cnt = 0;
        while(l <= r && q[r] == tmp) { cnt ++; r --; }
        if(r-l+1+cnt*2 <= k) {
            while(cnt --) q[++ r] = tmp-1, q[++ r] = tmp-1;
        } else {
            while(cnt --) q[++ r] = tmp;
            flag = 1;
        }
    }
    printf("Yes\n");
    for(int i = r; i >= l; i --) printf("%d ", q[i]); puts("");
    return 0;
}

C. Jamie and Interesting Graph

题意:

给一个n,m,让你构造一个n个点m条边的无向图,使得这个图的最小生成树和最短路都是质数。

做法:

简单构造。
先构一条链,前面全是1,最后补一个权值补成质数,然后剩下的边都赋1e9。
(比赛时手贱,一个>写成了==,然后gg。。)

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std;
typedef long long ll;

const int N = 1000010;
int n, m, cnt;
int vis[N], p[N];

void prepare(int n) {
    vis[1] = 1;
    for (int i = 2; i <= n; i ++){
        if (!vis[i]) p[++ cnt] = i;
        for (int j = 1; j <= cnt && i * p[j] <= n; j ++){
            vis[i * p[j]] = 1;
            if (!(i % p[j])) break;
        }
    }
}
int main() {
    prepare(1000000);
    scanf("%d%d", &n, &m);
    int ans = n-2; int x = upper_bound(p+1, p+1+cnt, ans)-p, y;
    int tmp = p[x]-ans; ans = p[x];
    printf("%d %d\n", ans, ans);
    for(int i = 1; i < n-1; i ++) printf("%d %d 1\n", i, i+1);
    printf("%d %d %d\n", n-1, n, tmp);
    x = 1, y = 3;
    for(int i = n; i <= m; i ++) {
        printf("%d %d 1000000000\n", x, y);
        y ++; if(y > n) x ++, y = x+2;//就是这里原来写了==n
    }
    return 0;
}

D. Jamie and To-do List

题意:

4种操作:
1. set a b 把a这个人的优先级更改为b.
2. query a 查询a这个人比它优先级小的人的数量,如果a不存在输出-1。
3. remove a 删除a这个人的信息。
4. undo x 撤销最后x步操作。
对于每个query输出答案,强制在线。

做法:

一眼看就是可持久化嘛。。。
具体来说就是建一棵可持久化权值线段树,保存权值的数量;再建一棵可持久化线段树,保存这个人的信息(优先级是多少,是否存在)。
然后两棵线段树可以合并起来写的,只要保存两个root就可以了。
(另外这题调了很久的样例,后来发现居然是一个手贱把”%d”写成了”&d”!!!- - -。。。)

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cctype>
#include<map>
using namespace std;
typedef long long ll;

const int N = 100010, M = 10000010;
const int inf = 1e9;
int n, tot, tot2, cnt;
char opt[10];
string s;
map<string, int> mp;
int rt[N], ls[M], rs[M], sum[M], rt2[N];

inline int getid(string s) {
    if(mp.count(s)) return mp[s];
    mp[s] = ++ cnt; return cnt;
}
inline void insert(int k, int &nk, int l, int r, int x, int v) {
    nk = ++ tot; sum[nk] = sum[k]+v; ls[nk] = ls[k]; rs[nk] = rs[k];
    if(l == r) return;
    int mid = l+r>>1;
    if(x <= mid) insert(ls[k], ls[nk], l, mid, x, v);
    else insert(rs[k], rs[nk], mid+1, r, x, v);
}
inline int query(int nk, int l, int r, int x, int y) {
    if(!nk) return 0;
    if(l >= x && r <= y) return sum[nk];
    int mid = l+r>>1; int ret = 0;
    if(x <= mid) ret += query(ls[nk], l, mid, x, y);
    if(y > mid) ret += query(rs[nk], mid+1, r, x, y);
    return ret;
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) {
        int x, b, c; scanf("%s", opt);
        rt[i] = rt[i-1]; rt2[i] = rt2[i-1];
        if(opt[0] == 's') {
            cin >> s; scanf("%d", &x); b = getid(s); int tmp = query(rt2[i], 1, n, b, b);
            if(tmp) insert(rt[i], rt[i], 1, inf, tmp, -1);
            insert(rt2[i], rt2[i], 1, n, b, x-tmp); insert(rt[i], rt[i], 1, inf, x, 1);
        } else if(opt[0] == 'r') {
            cin >> s; b = getid(s); int tmp = query(rt2[i], 1, n, b, b);
            if(tmp) insert(rt[i], rt[i], 1, inf, tmp, -1);
            insert(rt2[i], rt2[i], 1, n, b, -tmp);
        } else if(opt[0] == 'q') {
            cin >> s; b = getid(s); int tmp = query(rt2[i], 1, n, b, b);
            if(tmp == 0) puts("-1");
            else if(tmp == 1) puts("0");
            else printf("%d\n", query(rt[i], 1, inf, 1, tmp-1));
            fflush(stdout);
        } else { scanf("%d", &x); rt[i] = rt[i-x-1]; rt2[i] = rt2[i-x-1]; }
    }
    return 0;
}

E. Jamie and Tree

题意:

给一棵树,树上有点权。
三种操作:
1. 1 u 根换成u
2. 2 u v x 把最小的包含u,v的子树全加上x
3. 3 u 查询以u为根的子树的权值和
根一开始是1.

做法:

首先不可能真的去换根。。我们大力分类讨论。

发现2就是求出lca,以lca为根的子树加上x。
问题转化为换根,求lca,子树加/查询。

我们先按照根为1预处理出倍增表、深度、dfs序等东西。
设当前的根为rt。

求lca:
发现lca肯定是u-rt路径上距离v最近的点。
然后你把u-rt的lca求出来,分成两条链,两条链再分别讨论,求一下距离,等等。。
(是不是特别麻烦- -,所以不要管这个方法)

我们发现最终只有三个点有可能是答案,分别是lca(u,v),lca(u,rt),lca(v,rt).
并且这三个点有两个(或三个)是重复的,我们只要取那个不重复的(三个重复就取任意一个)就是答案。(简单吧qaq)
其实这个原理和上面的是差不多的,只是更简化了一下。
所以有时发现一些神奇的性质还是很有趣的qaq。

子树加/查询:
1. 假如x=rt,直接返回全局。
2. 假如x是rt的祖先,倍增找到rt-x路径上x的儿子y,答案是全局减去y的子树。
3. 否则就是正常的x的子树。

易错点:

  1. 这题有负权,于是某大佬写输优的时候没判负数gg了,qaq。
  2. 线段树里面要保证查询的区间不是空,如果x>y要及时返回0。(找这个错我还对拍了很久。。)

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cctype>
#define lc (o<<1)
#define rc (o<<1|1)
using namespace std;
typedef long long ll;

inline ll read() {
    char ch = getchar(); ll x = 0; int op = 1;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
    for(; isdigit(ch); ch = getchar()) x = x*10+ch-'0';
    return x*op;
}
inline void write(ll a) {
    if(a < 0) putchar('-'), a = -a;
    if(a >= 10) write(a/10); putchar('0'+a%10);
}

const int N = 200010;
int n, m, cnt, clk, rt;
int a[N], val[N], head[N], depth[N], f[N][25], in[N], out[N];
ll sum[N<<2], tag[N<<2];
struct edge {
    int to, nxt;
    edge() {}
    edge(int x, int y) { to = x, nxt = y; }
}e[N<<1];

//segment tree
inline void build(int o, int l, int r) {
    if(l == r) { sum[o] = val[l]; return; }
    int mid = l+r>>1;
    build(lc, l, mid); build(rc, mid+1, r);
    sum[o] = sum[lc]+sum[rc];
}
inline void pushdown(int o, int l, int r) {
    if(!tag[o]) return;
    tag[lc] += tag[o]; tag[rc] += tag[o];
    int mid = l+r>>1;
    sum[lc] += (ll)tag[o]*(mid-l+1); sum[rc] += (ll)tag[o]*(r-mid);
    tag[o] = 0;
}
inline void update(int o, int l, int r, int x, int y, int w) {
    if(x > y) return;
    if(l == x && r == y) {
        sum[o] += (ll)(r-l+1)*w; tag[o] += w; return;
    }
    pushdown(o, l, r);
    int mid = l+r>>1;
    if(y <= mid) update(lc, l, mid, x, y, w);
    else if(x > mid) update(rc, mid+1, r, x, y, w);
    else update(lc, l, mid, x, mid, w), update(rc, mid+1, r, mid+1, y, w);
    sum[o] = sum[lc]+sum[rc];
}
inline ll query(int o, int l, int r, int x, int y) {
    //printf("%d %d %d %d\n", l, r, x, y);
    if(x > y) return 0;
    if(l == x && r == y) return sum[o];
    pushdown(o, l, r);
    ll ret = 0; int mid = l+r>>1;
    if(y <= mid) return query(lc, l, mid, x, y);
    else if(x > mid) return query(rc, mid+1, r, x, y);
    else return query(lc, l, mid, x, mid) + query(rc, mid+1, r, mid+1, y);
}

inline void addedge(int x, int y) { e[++ cnt] = edge(y, head[x]); head[x] = cnt; }
inline bool isAncestor(int x, int y) { return in[x] <= in[y] && out[x] >= out[y]; }
inline void dfs(int u, int lst, int s) {
    depth[u] = s; f[u][0] = lst; in[u] = ++ clk; val[clk] = a[u];
    for(int i = head[u]; i; i = e[i].nxt) if(e[i].to != lst) dfs(e[i].to, u, s+1);
    out[u] = clk;
}
inline int lca(int x, int y) {
    if(depth[x] < depth[y]) swap(x, y);
    int tmp = depth[x]-depth[y];
    for(int i = 20; i >= 0; i --)
        if(tmp>>i&1) x = f[x][i];
    if(x == y) return x;
    for(int i = 20; i >= 0; i --)
        if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
    return f[x][0];
}
inline int getlca(int rt, int x, int y) {
    int u = lca(x, rt), v = lca(y, rt), w = lca(x, y);
    return u^v^w;
}
inline int find(int x, int y) {
    int tmp = depth[x]-depth[y]-1;
    for(int i = 20; i >= 0; i --)
        if(tmp>>i&1) x = f[x][i];
    return x;
}
inline void add(int rt, int x, int w) {
    if(rt == x) update(1, 1, n, 1, n, w);
    else if(!isAncestor(x, rt)) update(1, 1, n, in[x], out[x], w);
    else {
        int y = find(rt, x);
        update(1, 1, n, 1, in[y]-1, w);
        update(1, 1, n, out[y]+1, n, w);
    }
}
inline ll qry(int rt, int x) {
    if(rt == x) return sum[1];
    else if(!isAncestor(x, rt)) return query(1, 1, n, in[x], out[x]);
    else {
        int y = find(rt, x); //printf("y = %d\n", y);
        return query(1, 1, n, 1, in[y]-1) + query(1, 1, n, out[y]+1, n);
    }
}
int main() {
    /*freopen("E.in", "r", stdin);
    freopen("E.out", "w", stdout);*/
    n = read(), m = read();
    for(int i = 1; i <= n; i ++) a[i] = read();
    for(int i = 1; i < n; i ++) {
        int x = read(), y = read();
        addedge(x, y); addedge(y, x);
    }
    rt = 1;
    dfs(1, 0, 0);
    //for(int i = 1; i <= n; i ++) printf("%d %d\n", in[i], out[i]);
    build(1, 1, n);
    for(int j = 1; j <= 20; j ++)
        for(int i = 1; i <= n; i ++) f[i][j] = f[f[i][j-1]][j-1];
    while(m --) {
        int opt = read(), x, y, z, w;
        if(opt == 1) rt = read();
        else if(opt == 2) {
            x = read(), y = read(), w = read();
            z = getlca(rt, x, y);
            add(rt, z, w);
        } else {
            x = read();
            write(qry(rt, x)); puts("");
        }
    }
    return 0;
}

总结:

emmmm。。rk,rating什么的就没有了。
比赛时只过了A(??!!)
首先B出了bug我也没办法。
C手贱我也不想说什么。
E题赛时写完了(当然是写错了)没调出来,而且那时时间也不多了。
我只想说emm,还好是unrated..qaq。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值