这次还好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的子树。
易错点:
- 这题有负权,于是某大佬写输优的时候没判负数gg了,qaq。
- 线段树里面要保证查询的区间不是空,如果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。