• 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
    */
    
  • 相关阅读:
    Object类的方法简记
    run()和start()调用问题简记
    面向对象设计
    关于JVM的简记
    操作运算符的简记
    jdbc简记
    synchronized的简记
    垃圾回收的一些简记
    java.io.IOException: Stream closed
    Java-文件名、目录名或卷标语法不正确
  • 原文地址:https://www.cnblogs.com/hjmmm/p/10863324.html
Copyright © 2020-2023  润新知