Codeforces Round #545 (Div. 1)
发现自己少写了一场 补一波
比赛链接
A
给定一个n∗m的表格,对于每个位置(x, y)
求最小的t
使得x行和y列的数被编上1~t之后 相互大小关系不变
\(1 \le n, m \le 1000\)
分别离散一下
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e3 + 5;
const int inf = 0x3f3f3f3f;
int n, m;
struct Line{
long long w[N];
int size;
void st(int x){
sort(w + 1, w + x + 1);
size = unique(w + 1, w + x + 1) - w - 1;
}
int pos(int y){
return lower_bound(w + 1, w + size + 1, y) - w;
}
}la[N], lb[N];
long long mp[N][N];
int a[N][N], b[N][N];
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= m; ++j){
scanf("%lld", &mp[i][j]);
}
}
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= m; ++j){
la[i].w[j] = mp[i][j];
}
la[i].st(m);
}
for(int i = 1; i <= m; ++i){
for(int j = 1; j <= n; ++j){
lb[i].w[j] = mp[j][i];
}
lb[i].st(n);
}
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= m; ++j){
a[i][j] = la[i].pos(mp[i][j]);
b[i][j] = lb[j].pos(mp[i][j]);
}
}
for(int i = 1, xx; i <= n; ++i){
for(int j = 1; j <= m; ++j){
//printf("j = %d ", j);
int t1 = a[i][j];
int t2 = b[i][j];
int t3 = la[i].size - a[i][j];
int t4 = lb[j].size - b[i][j];
printf("%d ", max(t1, t2) + max(t3, t4));
}
printf("\n");
}
return 0;
}
B
给定01串S和T
把S打乱顺序,使得T在S中出现的次数最多
\(1 \leqslant |s|, |T| \leqslant 500\,000\)
把T kmp一下 尽可能多的出现 剩余0, 1接在后面
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 5e5 + 5;
const int inf = 0x3f3f3f3f;
int n, m, k, top;
char s[N], t[N], stk[N];
int c00, c01, c10, c11, c20, c21;
int nxt[N];
void init(){
for(int i = 2; i <= m; ++i){
while(k && t[k + 1] != t[i]) k = nxt[k];
if(t[k + 1] == t[i]) ++k;
nxt[i] = k;
}
for(int i = 1; i <= m; ++i) if(t[i] == '0') ++c00; else ++c01;
for(int i = k + 1; i <= m; ++i) if(t[i] == '0') ++c10; else ++c11;
for(int i = 1; i <= n; ++i) if(s[i] == '0') ++c20; else ++c21;
}
int main(){
scanf("%s%s", s + 1, t + 1);
n = strlen(s + 1), m = strlen(t + 1);
init();
if(c20 < c00 || c21 < c01){
puts(s + 1); return 0;
}
c20 -= c00, c21 -= c01;
for(int i = 1; i <= m; ++i) stk[++top] = t[i];
while(c20 >= c10 && c21 >= c11){
c20 -= c10, c21 -= c11;
for(int i = k + 1; i <= m; ++i) stk[++top] = t[i];
}
while(c20) --c20, stk[++top] = '0';
while(c21) --c21, stk[++top] = '1';
stk[++top] = '\0';
puts(stk + 1);
return 0;
}
C
n点m边有向图,第0天从1号点出发
每个点都有一个博物馆,以每周d天为周期开放
每个博物馆有一个长度为d的01串表示一周的开放状态
求一个旅行线路,使得访问不同博物馆最多。
\(1 \leq n \leq 100\,000, 0 \leq m \leq 100\,000, 1 \leq d \leq 50\)
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <bitset>
#include <set>
#define md(x) (x) >= d ? (x) - d : (x)
#define mp(x, y) make_pair(x, y)
#define fir first
#define sec second
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int n, m, d;
char str[N][55];
vector<int> in[N], out[N];
int t1, t2;
PII s1[N * 50], s2[N * 50];
bool vis[N][55]; int tag[N];
int col[N][52], colsize, f[N * 50], cnt[N * 50];
/*
发现了一个神奇的东西
随便弄一棵生成树 编上dfs序
dfs逆序找点 点标记后找入边 如果有点没被标记而且有入边通向自己 那么必然有环
这样就可以找强连通分量啦
如果一个点编号比自己大 到自己又有边 如果不是有一条从根经过自己到这个点的路在树上
自己就是这个点编号加一了
然后按照这样的逆序找到的强连通分量刚好又是拓扑逆序
妙极了!
*/
void dfs1(int x, int y){
vis[x][y] = 1;
for(int i : out[x]) if(!vis[i][md(y + 1)]) dfs1(i, md(y + 1));
s1[++t1] = mp(x, y);
}
void dfs2(int x, int y){
col[x][y] = colsize;
if(str[x][y] == '1' && tag[x] != colsize)
tag[x] = colsize, ++cnt[colsize];
s2[++t2] = mp(x, y);
for(int i : in[x]) if(!col[i][md(y + d - 1)]) dfs2(i, md(y + d - 1));
}
int main(){
scanf("%d%d%d", &n, &m, &d);
for(int i = 1, x, y; i <= m; ++i)
scanf("%d%d", &x, &y), in[y].push_back(x), out[x].push_back(y);
for(int i = 1; i <= n; ++i) scanf("%s", str[i]);
for(int i = 1; i <= n; ++i)
for(int j = 0; j < d; ++j)
if(!vis[i][j]) dfs1(i, j);
for(int i = t1; i >= 1; --i) if(!col[s1[i].fir][s1[i].sec])
++colsize, dfs2(s1[i].fir, s1[i].sec);
for(int i = t2, x, y, z; i >= 1; --i){
x = s2[i].fir, y = s2[i].sec, z = 0;
for(int j : out[x]) if(col[j][md(y + 1)] != col[x][y])
z = max(z, f[col[j][md(y + 1)]]);
f[col[x][y]] = max(f[col[x][y]], cnt[col[x][y]] + z);
}
/*for(int i = 1; i <= n; ++i)
for(int j = 0; j < d; ++j)
printf("%d %d %d %d\n", i, j, col[i][j], cnt[i][j]);*/
printf("%d\n", f[col[1][0]]);
return 0;
}
D
人类智慧交互题
游戏图由一个长度为t的链和一个大小为c的环组成
链的左端点是起点,链与环的交界处是终点
最初起点有10个棋子0~9每次可以指定一些棋子走一步
返回是几个集合 每个集合中的编号在一个点上
当你把所有棋子都放在终点 返回done
\(1 \leq t, c \leq 1000 Q \leq 3(t + c)\)
用两个点先跳 一个一步(x)一个两步(y)
然后当x第一次到终点时 y在终点后p处
这时关于y有 t = c * k + p
那么再跳c - p周期 y追上x 此时两点都在终点后c - p处
这时把所有点前移 其他点走t次时 这两点走了c * k + p次
刚好从c - p位置走到终点
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <bitset>
#include <set>
using namespace std;
char guo[5000];
int main(){
while(1){
printf("next 1 0\n"); fflush(stdout);
cin.getline(guo, 3000);
printf("next 1\n"); fflush(stdout);
int x; scanf("%d", &x); cin.getline(guo, 3000);
if(x == 2) break;
}
while(1){
printf("next 0 1 2 3 4 5 6 7 8 9\n"); fflush(stdout);
int x; scanf("%d", &x); cin.getline(guo, 3000);
if(x == 1) break;
}
printf("done\n");
return 0;
}
E
翻译来自yyb
你有一列有n个车厢的火车,从车头开始1−n编号。
现在有3种操作,第一种是在车头位置加入k节车厢,第二种位置是在车尾位置加入k节车厢,第三种是修改每节车厢的价值。
价值是这样子来的:一开始所有车厢的价值都是0(包括中途加入的车厢),然后每次修改会给定s,b,如果当前车厢是从车头开始数的第i节,那么它的价值就会加上(i−1)∗s+b。
在每次操作结束之后回答价值最小的车厢的编号以及其价值。如果有多个输出编号最小的那个。
每次插入的区间递增
所以只需维护最左边的一个
然后手玩一下可以造一个下凸包(纵坐标是插入时函数值的负值,用于抵消之前这个位置贡献)
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <bitset>
#include <set>
#define mp(x, y) make_pair(x, y)
#define fir first
#define sec second
#define calc(x) (k * x.fir + x.sec + b)
#define Slope(x, y) 1.0 * (x.sec - y.sec) / (x.fir - y.fir)
using namespace std;
const int N = 3e5 + 5;
typedef long long ll;
typedef pair<ll, ll> PLL;
ll b, k, n;
int m, top;
PLL stk[N];
int main(){
scanf("%lld%d", &n, &m); top = 1, b = k = 0, stk[1] = mp(0, 0);
for(int i = 1, opt, x, y; i <= m; ++i){
scanf("%d%d", &opt, &x);
if(opt == 1) top = 1, b = k = 0, stk[1] = mp(0, 0), n += x;
else if(opt == 2){
// printf("n = %d\n", n);
PLL cur = mp(n, -(k * n + b)); n += x;
while(top > 1 && Slope(stk[top], stk[top - 1]) >= Slope(cur, stk[top])) --top;
stk[++top] = cur;
}
else scanf("%d", &y), b += x, k += y;
//for(int i = 1; i <= top; ++i) printf("%lld %lld | ", stk[i].fir, stk[i].sec);
//printf("b = %lld k = %lld\n", b, k);
int tt = top;
while(tt > 1 && calc(stk[tt - 1]) <= calc(stk[tt])) --tt;//
printf("%lld %lld\n", stk[tt].fir + 1, calc(stk[tt]));
//printf("------------------\n");
}
return 0;
}
F
最初i节点优先级为i
每次烧掉一个优先级最小的叶子
有三种操作
up 把x节点的优先级变成当前最大优先级+1
when 询问x节点第几个被烧掉
compare 比较x, y两点哪个先被烧掉
维护一棵lct 树根是最后一个被烧掉的点
最初每个点的颜色是子树最大优先级
(因为不可能从根烧过来啊
这样就有两个性质
颜色比自己小的点一定被自己早烧掉
同时只有根的颜色是最大优先级 (也就是最大优先级只有一个)
然后如果up的话
就相当于先烧掉别的点 然后从根一路烧过来
所以到根路径都被标上根的颜色 x标上根的染色加一 换根 就完成啦
由于满足烧每条splay维护的同颜色边时
都是从深度大到深度小烧(因为不可能从根烧过来啊
所以颜色小的先烧 颜色和自己一样的 位置在右子树的先烧 加上自己就好啦
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <bitset>
#include <set>
#define md(x) ((x) % d)
#define mp(x, y) make_pair(x, y)
#define fir first
#define sec second
#define rs(x) ch[x][1]
#define ls(x) ch[x][0]
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 5;
int stk[N], top;
struct BIT{
int n, w[N << 1];
inline void mdf(int x, int d){while(x < n){w[x] += d, x += x & -x;}}
inline int qry(int x){int res = 0; while(x){res += w[x], x -= (x & -x);} return res;}
}bit;
int n, m, colcnt, id[N];
struct LCT{
int ch[N][2], fa[N], sz[N], rev[N], tag[N], col[N];
inline bool get(int x){return x == ch[fa[x]][1];}
inline bool is_root(int x){return (x != ch[fa[x]][0]) && (x != ch[fa[x]][1]);}
inline void update(int x){if(x) sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;}
inline void rv(int x){if(x) swap(ls(x), rs(x)), rev[x] ^= 1;}
inline void cov(int x, int y){col[x] = tag[x] = y;}
inline void pushdown(int x){
if(rev[x]) rv(ls(x)), rv(rs(x)), rev[x] = 0;
if(tag[x]) cov(ls(x), tag[x]), cov(rs(x), tag[x]), tag[x] = 0;
}
inline void rotate(int x){
int y = fa[x], z = fa[y], k = get(x);
fa[x] = z; if(!is_root(y)) ch[z][get(y)] = x;
fa[ch[x][k ^ 1]] = y, ch[y][k] = ch[x][k ^ 1],
fa[y] = x, ch[x][k ^ 1] = y,
update(y), update(x);
}
inline void splay(int x){
stk[top = 1] = x;
for(int i = x; !is_root(i); i = fa[i]) stk[++top] = fa[i]; while(top) pushdown(stk[top--]);
for(int y; !is_root(x); rotate(x)) if(!is_root(y = fa[x])) rotate(get(y) == get(x) ? y : x);
}
void print(){
printf("--------print--------\n");
for(int i = 1; i <= n; ++i)
printf("%d: %d %d %d %d %d\n", i, ls(i), rs(i), sz[i], col[i], rev[i]);
printf("---------end---------\n");
}
inline void access(int x, int y){
for(int i = 0; x; i = x, x = fa[x]){
splay(x), pushdown(x), rs(x) = 0, update(x);
bit.mdf(col[x], -sz[x]), cov(x, y), bit.mdf(col[x], sz[x]);
rs(x) = i, update(x);
}
}
inline void evert(int x, int y){access(x, y), splay(x), rv(x);}
inline void nwcol(int x, int y){
pushdown(x), rs(x) = 0, update(x);
bit.mdf(col[x], -1), cov(x, y), bit.mdf(col[x], 1);
}
inline int qry(int x){splay(x)/*, print()*/; /*printf("qry %d: col %d ls %d\n", x, col[x], ls(x)); */return bit.qry(col[x]) - sz[ls(x)];}
}lct;
struct Edge{int v, next;}edge[N << 1];
int head[N], esize;
inline void addedge(int x, int y){
edge[++esize] = (Edge){y, head[x]}, head[x] = esize;
}
void dfs(int x, int ff){
id[x] = x, lct.sz[x] = 1;
for(int i = head[x], vv; ~i; i = edge[i].next){
vv = edge[i].v; if(vv == ff) continue;
dfs(vv, x), lct.fa[vv] = x, id[x] = max(id[vv], id[x]);
}
for(int i = head[x], vv; ~i; i = edge[i].next){
vv = edge[i].v; if(vv == ff) continue;
if(id[vv] == id[x]){
lct.rs(x) = vv, lct.sz[x] += lct.sz[vv]; break;
}
}
lct.col[x] = id[x], bit.mdf(id[x], 1);
// printf("dfs node%d : col %d sz %d bit %d\n", x, lct.col[x], lct.sz[x], bit.qry(id[x]));
}
int main(){
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m); bit.n = (n + m);
for(int i = 1, x, y; i < n; ++i)
scanf("%d%d", &x, &y), addedge(x, y), addedge(y, x);
dfs(n, 0);
char opt[20]; int x, y; colcnt = n;
for(int i = 1; i <= m; ++i){
scanf("%s", opt + 1);
if(opt[1] == 'u'){
scanf("%d", &x);
lct.evert(x, colcnt), lct.nwcol(x, ++colcnt);
}
else if(opt[1] == 'w'){
scanf("%d", &x);
printf("%d\n", lct.qry(x));
}
else {
scanf("%d%d", &x, &y);
printf("%d\n", (lct.qry(x) < lct.qry(y)) ? x : y);
}
}
return 0;
}
/*
8 20
4 6
2 4
1 5
2 7
1 2
1 8
2 3
compare 4 8
when 3
compare 2 6
compare 2 3
compare 4 5
when 1
compare 3 1
when 3
up 6
when 1
compare 2 1
when 5
compare 4 1
up 7
when 8
compare 5 2
up 3
when 6
up 6
when 7
*/