Codeforces Round #545 (Div. 1)

发现自己少写了一场 补一波

比赛链接

cf

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("
");
	}
    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] = '';
	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
", i, j, col[i][j], cnt[i][j]);*/
	
	printf("%d
", 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
"); fflush(stdout);
	    cin.getline(guo, 3000);
	    printf("next 1
"); 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
"); fflush(stdout);
	    int x; scanf("%d", &x); cin.getline(guo, 3000);
		if(x == 1) break;	
	}
	printf("done
");
	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);
			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
", b, k);
		int tt = top;
		while(tt > 1 && calc(stk[tt - 1]) <= calc(stk[tt])) --tt;//
		printf("%lld %lld
", stk[tt].fir + 1, calc(stk[tt]));
		//printf("------------------
");
	}
	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--------
");
    	for(int i = 1; i <= n; ++i)
    	    printf("%d: %d %d %d %d %d
", i, ls(i), rs(i), sz[i], col[i], rev[i]);
    	printf("---------end---------
");
    } 
 	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
", 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
", 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
", lct.qry(x));
		}
		else {
			scanf("%d%d", &x, &y);
			printf("%d
", (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
*/
原文地址:https://www.cnblogs.com/hjmmm/p/10863324.html