zoukankan      html  css  js  c++  java
  • 2018冬令营模拟测试赛(十一)

    2018冬令营模拟测试赛(十一)

    [Problem A]小Y增员操直播群

    试题描述

    QAQ

    Q_Q

    QwQ

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    这题其实就是个暴力……

    从后往前扫一遍,对于编号为 (i) 的人,找到和他互膜的编号最小的人 (j),那么 (j)(i) 一定属于不同组。但注意这时 (j) 不一定是左边组的最后一个成员,所以需要往前推,一直到某个 (i') 对应的 (j' = 0) 之后,再下一个对应的互膜的人才是左边组的最后一个成员……

    情况挺多的总之想到从后往前扫就不难做了。(为什么从后往前?因为右边组的人数大于等于左边的人数,所以右边每个人有且仅有一次和左边的人互膜)

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <vector>
    using namespace std;
    #define rep(i, s, t) for(int i = (s); i <= (t); i++)
    #define dwn(i, s, t) for(int i = (s); i >= (t); i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 100010
    
    int n, m, hd[maxn];
    vector <int> lft[maxn];
    
    int solve(int l, int r, int dep) {
    	// printf("solve [%d, %d] %d
    ", l, r, dep);
    	if(l == r) return hd[r] >= lft[r].size() ? dep : -1;
    	int mx = -1, i;
    	for(i = r; i > mx; i--) {
    		if(hd[i] >= lft[i].size()) return -1;
    		int mylft = lft[i][hd[i]++];
    		if(mx < 0) mx = mylft;
    		if(mx - mylft != r - i) return -1;
    		if(!(mylft - l) && i > mx + 1) {
    			i--;
    			if(hd[i] >= lft[i].size()) return -1;
    			mx = lft[i][hd[i]];
    			break;
    		}
    	}
    	for(; i > mx; i--) if(hd[i] >= lft[i].size() || lft[i][hd[i]++] - l != (i - l) % (mx - l + 1)) return -1;
    	if(mx - l + 1 > r - mx) return -1;
    	int lans = solve(l, mx, dep + 1), rans;
    	if(lans < 0) return lans;
    	rans = solve(mx + 1, r, dep + 1);
    	if(rans < 0) return rans;
    	return max(lans, rans);
    }
    
    void work() {
    	rep(i, 0, n - 1) lft[i].clear();
    	
    	bool ok = 1;
    	n = read(); m = read();
    	rep(i, 1, m) {
    		int a = read(), b = read();
    		if(a == b) ok = 0;
    		if(a > b) swap(a, b);
    		lft[b].push_back(a);
    	}
    	if(!ok) return (void)puts("-1");
    	rep(i, 0, n - 1) sort(lft[i].begin(), lft[i].end()), hd[i] = 0;
    	
    	int ans = solve(0, n - 1, 0);
    	printf("%d
    ", ans);
    	
    	return ;
    }
    
    int main() {
    	int T = read();
    	
    	while(T--) work();
    	
    	return 0;
    }
    

    [Problem B]小J真爱粉交流群

    试题描述

    TwT

    TAT

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    仔细想来,有这么几个性质:

    • 每次大敦操作后,小 J 至多放一堵墙(首先一堵一堵放肯定来得及;如果一下放很多反而可能起不到“诱导”作用,如果第一步把墙放完,那么大墩的路径已经固定下来了,大墩就可以针对这个路径走一条最短路;如果一步一步放,大墩还按照贪心的最短路来,就可以把他逼到一个角然后再返回,从而增大他的得分);

    • 每次小 J 如果放墙,一定放在大墩的棋子下面(这个显然);

    • 如果小 J 不放墙,大墩一定往下走一格(好不容易给一个不会被逼到角的机会一定得抓住,因为往下走一行无论初始位置在哪小 J 都有办法把大墩逼到小 J 想让他去的位置);

    • 任何时刻大墩棋子下方的一排墙一定是一段连续的区间(根据上两条可得)。

    (至于那个大墩不可能往上走啥的就不必再提了……)

    于是针对这几条性质可以设计一个 dp。(f(i, l, r)) 表示棋子处于第 (i) 行,该行下方的墙铺在了区间 ([l, r]),棋子在左边(即坐标 ((i, l - 1)));(g(i, l, r)) 表示棋子处于第 (i) 行,该行下方墙铺在了区间 ([l, r]),棋子在右边(即坐标 ((i, r + 1)))。

    因为是大墩先选一个位置放棋子,所以转移的时候是考虑小 J 决策是否在他棋子下面放墙。

    • 若放墙,则轮到大墩决策走到墙的最左端还是最右端,这两种决策取较小值;

    • 若不放墙,则大墩往下走一格;

    放、不放墙两种决策取最大值。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <vector>
    using namespace std;
    #define rep(i, s, t) for(int i = (s); i <= (t); i++)
    #define dwn(i, s, t) for(int i = (s); i >= (t); i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 110
    #define oo 2147483647
    
    int n, m, A[maxn][maxn], S[maxn][maxn], f[maxn][maxn][maxn], g[maxn][maxn][maxn]; // f for left, g for right
    
    void upmx(int& a, int b) {
    	if(a < b) a = b;
    	return ;
    }
    void upmn(int& a, int b) {
    	if(a > b) a = b;
    	return ;
    }
    
    void dp(int i, int l, int r) {
    	if((l - 1 >= m || l - 1 < 1 || f[i][l][r] >= 0) && (r + 1 <= 1 || r + 1 > m || g[i][l][r] >= 0)) return ;
    	if(i == n) return (void)(f[i][l][r] = g[i][l][r] = 0);
    	int t1, t2;
    	if(1 <= l - 1 && l - 1 < m) {
    		t1 = oo; t2 = -1;
    		if(l > 1 && !(l - 1 == 1 && max(r, l - 1) == m)) {
    			dp(i, l - 1, max(r, l - 1));
    			if(1 <= l - 2) upmn(t1, f[i][l-1][max(r,l-1)] + A[i][l-2]); // , printf("%d %d %d toleft %d %d %d
    ", i, l, r, i, l - 1, max(r, l - 1));
    			if(max(r, l - 1) + 1 <= m) upmn(t1, g[i][l-1][max(r,l-1)] + S[i][max(r,l-1)+1] - S[i][l-1]); // , printf("%d %d %d toright %d %d %d
    ", i, l, r, i, l - 1, max(r, l - 1));
    		}
    		if(t1 == oo) t1 = -1;
    		dp(i + 1, l, 0);
    		t2 = f[i+1][l][0] + A[i+1][l-1];
    		f[i][l][r] = max(t1, t2);
    	}
    	if(m >= r + 1 && r + 1 > 1) {
    		t1 = oo; t2 = -1;
    		if(r < m && !(min(l, r + 1) == 1 && r + 1 == m)) {
    			dp(i, min(l, r + 1), r + 1);
    			if(r + 2 <= m) upmn(t1, g[i][min(l,r+1)][r+1] + A[i][r+2]); // , printf("%d %d %d toright %d %d %d
    ", i, l, r, i, min(l, r + 1), r + 1);
    			if(1 <= min(l, r + 1) - 1) upmn(t1, f[i][min(l,r+1)][r+1] + S[i][r] - S[i][min(l,r+1)-2]); // , printf("%d %d %d toleft %d %d %d
    ", i, l, r, i, min(l, r + 1), r + 1);
    		}
    		if(t1 == oo) t1 = -1;
    		dp(i + 1, m + 1, r);
    		t2 = g[i+1][m+1][r] + A[i+1][r+1];
    		g[i][l][r] = max(t1, t2);
    	}
    	/*printf("hasf: %d, hasg: %d
    ", 1 <= l - 1 && l - 1 < m, m >= r + 1 && r + 1 > 1);
    	printf("[%d][%d][%d] %d %d
    ", i, l, r, f[i][l][r], g[i][l][r]); // */
    	return ;
    }
    
    void work() {
    	n = read(); m = read();
    	rep(i, 1, n) rep(j, 1, m) A[i][j] = read(), S[i][j] = S[i][j-1] + A[i][j];
    	if(m == 1) {
    		int sum = 0;
    		rep(i, 1, n) sum += A[i][1];
    		printf("%d
    ", sum);
    		return ;
    	}
    	memset(f, -1, sizeof(f)); memset(g, -1, sizeof(g));
    	int ans = oo;
    	rep(i, 1, m) {
    		if(i < m) dp(1, i + 1, 0), upmn(ans, f[1][i+1][0] + A[1][i]);
    		if(i > 1) dp(1, m + 1, i - 1), upmn(ans, g[1][m+1][i-1] + A[1][i]);
    	}
    	printf("%d
    ", ans);
    	return ;
    }
    
    int main() {
    	int T = read();
    	
    	while(T--) work();
    	
    	return 0;
    }
    

    [Problem C]青青草原播种计划

    试题描述

    ToT

    Q_T

    TAQ

    QAT

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    这就是一道大数据结构题咯(其实也不算太大)。

    这个可持久化是假的,只有询问,不要被吓到……直接对于每个助手开一个 vector,时间倒流的时候二分一下就好了。

    询问是经典的求 (mex)(最小没出现的数字)和最小不可表数。

    第一问直接线段树维护最长前缀即可,第二问我用的 (O(mathrm{log}^2n)) 的做法,先假设 (ans = 0),然后若 (sum[1, ans + 1] > ans) 就更新 (ans = sum[1, ans + 1])(sum[l, r]) 表示权值在 ([l, r]) 中的数的和),否则跳出(由于每次至少增加一倍,所以至多调用 (mathrm{log}n)(sum[l, r]))。只需要维护区间最长连续前缀和区间和即可。

    事实上第二问可以也用线段树维护做到一个 (mathrm{log}):考虑每个节点如果左儿子都能表出,且左儿子区间和大于等于右儿子区间最小值,则该节点的权值都能够被表出。

    合并的时候可以直接用线段树合并。(这好像是我第一次成功写出线段树合并,居然还是考场上写出来的……)

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <vector>
    using namespace std;
    #define rep(i, s, t) for(int i = (s); i <= (t); i++)
    #define dwn(i, s, t) for(int i = (s); i >= (t); i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 500010
    #define maxnode 10000010
    #define maxv 500000
    #define LL long long
    
    int n, rt[maxn];
    struct Info {
    	int time, ans1; LL ans2;
    	Info() {}
    	Info(int _1, int _2, LL _3): time(_1), ans1(_2), ans2(_3) {}
    	bool operator < (const Info& t) const { return time < t.time; }
    };
    vector <Info> tline[maxn];
    
    int ToT, lc[maxnode], rc[maxnode], pre[maxnode];
    LL sumv[maxnode];
    void update(int &o, int l, int r, int p, int v) {
    	if(!o) o = ++ToT;
    	if(l == r) {
    		if(!sumv[o] && v < 0) return ;
    		sumv[o] += v * p;
    		if(sumv[o]) pre[o] = 1; else pre[o] = 0;
    		return ;
    	}
    	int mid = l + r >> 1;
    	if(p <= mid) update(lc[o], l, mid, p, v);
    	else update(rc[o], mid + 1, r, p, v);
    	sumv[o] = sumv[lc[o]] + sumv[rc[o]];
    	pre[o] = (lc[o] && pre[lc[o]] == mid - l + 1) ? (rc[o] ? pre[rc[o]] : 0) + mid - l + 1 : (lc[o] ? pre[lc[o]] : 0);
    	return ;
    }
    LL qsum(int o, int l, int r, int ql, int qr) {
    	if(!o) return 0;
    	if(ql <= l && r <= qr) return sumv[o];
    	int mid = l + r >> 1;
    	LL ans = 0;
    	if(ql <= mid) ans += qsum(lc[o], l, mid, ql, qr);
    	if(qr > mid) ans += qsum(rc[o], mid + 1, r, ql, qr);
    	return ans;
    }
    LL query(int o) {
    	LL ans = pre[o];
    	while(1) {
    		LL org = ans;
    		ans = qsum(o, 1, maxv, 1, min(ans + 1, (LL)maxv));
    		if(ans == org) break;
    		if(ans >= maxv){ ans = qsum(o, 1, maxv, 1, maxv); break; }
    	}
    	return ans + 1;
    }
    void merge(int& y, int x, int l, int r) {
    	if(!y){ y = x; return ; }
    	if(!x) return ;
    	if(l == r) {
    		sumv[y] += sumv[x];
    		if(sumv[y]) pre[y] = 1; else pre[y] = 0;
    		return ;
    	}
    	int mid = l + r >> 1;
    	merge(lc[y], lc[x], l, mid); merge(rc[y], rc[x], mid + 1, r);
    	sumv[y] = sumv[lc[y]] + sumv[rc[y]];
    	pre[y] = (lc[y] && pre[lc[y]] == mid - l + 1) ? (rc[y] ? pre[rc[y]] : 0) + mid - l + 1 : (lc[y] ? pre[lc[y]] : 0);
    	return ;
    }
    void getEverything(int o, int l, int r) {
    	if(!o) return ;
    	if(l == r) rep(i, 1, sumv[o] / r) printf("%d ", l);
    	else {
    		int mid = l + r >> 1;
    		getEverything(lc[o], l, mid); getEverything(rc[o], mid + 1, r);
    	}
    	return ;
    }
    
    int lst;
    int Get() {
    	return (read() + lst - 1) % n + 1;
    }
    
    int main() {
    	n = read(); int q = read();
    	rep(i, 1, n) tline[i].push_back(Info(0, 1, 1));
    	rep(kase, 1, q) {
    		int tp = read(), u = Get(), v, g;
    		if(tp == 1) {
    			g = read();
    			// printf("processing: [%d] %d %d %d
    ", kase, tp, u, g);
    			update(rt[u], 1, maxv, g, 1);
    			// printf("rt[%d] = %d
    ", u, rt[u]);
    			tline[u].push_back(Info(kase, pre[rt[u]] + 1, query(rt[u])));
    		}
    		if(tp == 2) {
    			g = read();
    			// printf("processing: [%d] %d %d %d
    ", kase, tp, u, g);
    			update(rt[u], 1, maxv, g, -1);
    			tline[u].push_back(Info(kase, pre[rt[u]] + 1, query(rt[u])));
    		}
    		if(tp == 3) {
    			v = Get();
    			// printf("processing: [%d] %d %d %d
    ", kase, tp, u, v);
    			swap(u, v);
    			if(u == v) continue;
    			// u -> v
    			merge(rt[v], rt[u], 1, maxv);
    			rt[u] = 0;
    			tline[v].push_back(Info(kase, pre[rt[v]] + 1, query(rt[v])));
    			tline[u].push_back(Info(kase, pre[rt[u]] + 1, query(rt[u])));
    		}
    		if(tp == 4) {
    			// printf("processing: [%d] %d %d
    ", kase, tp, u);
    			vector <Info> :: iterator it = tline[u].end(); it--;
    			printf("%d %lld
    ", lst = it->ans1, it->ans2);
    		}
    		if(tp == 5) {
    			g = read();
    			// printf("processing: [%d] %d %d %d
    ", kase, tp, u, g);
    			vector <Info> :: iterator it = lower_bound(tline[u].begin(), tline[u].end(), Info(g, 0, 0)); it--;
    			printf("%d %lld
    ", lst = it->ans1, it->ans2);
    		}
    	}
    	rep(i, 1, n) {
    		getEverything(rt[i], 1, maxv);
    		puts("0");
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    Java入门第二阶段总结
    POj3017 dp+单调队列优化
    CH5701 开车旅行(倍增dp+set)
    dp优化---四边形不等式与决策单调性
    CH5E07 划分大理石(背包dp+二进制拆分)
    POJ1185 炮兵阵地(状压dp)
    CH5501 环路运输(单调栈)
    poj3585 Accumulation Degree(换根dp)
    CH5105 Cookies (线性dp)
    反射
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8240917.html
Copyright © 2011-2022 走看看