ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【总结】IOI2020 Day1

2022-03-21 17:05:34  阅读:166  来源: 互联网

标签:总结 return val get int rep Day1 IOI2020 ls


T1: [IOI2020]植物比较

难度很高的思维题。

构造出一种可行方案满足 \(r\) 的限制是不难的。\(r\) 中存在若干个 \(0\),如果 \(r_i = 0\) 表示它后面 \(k-1\) 个数都比它小,所以我们找到一个 \(0\),它前面 \(k-1\) 个位置都不为 \(0\),那么它就是整个序列的最大值。将最大值删掉归纳下去即可。

当 \(2k>n\) 时满足条件的 \(0\) 只有 \(1\) 个,当 \(2k\le n\) 时可能有多个,但它们之间距离都 \(\ge k\),也就意味着它们没有直接比较,谁更大一点没有区别。

这样我们就解决了 Sub2,3。对于 Sub 4,由于没有 \(Ans = 0\) 的情况,我们构造出的排列的大小关系就是答案。

回到 Sub1,\(k=2\) 表示邻项比较。所以 \(i,j\) 能比较大小当且仅当 \(a_i>a_{i+1}>\cdots>a_{j}\) 或者 \(a_i>a_{i-1}>\cdots>a_{j}\) 或者 \(a_i<a_{i+1}<\cdots<a_{j}\) 或者 \(a_i<a_{i-1}<\cdots<a_{j}\)。也就是存在一条链,可以直接 \(O(N)\) 预处理每条链左右端点即可。

那么对于一般情况,能够比较大小关系就是存在 \(a_{i}\to a_{k_1}\to \cdots\to a_{k_e}\to a_{j}\) 的关系,当 \(|i-j|<k\) 能够直接比较大小关系,所以我们从点 \(i\) 开始,每次在后面 \(k-1\) 个数中找最大的小于自己的数/最小的大于自己的数,一直往后跳,直到点 \(j\) 的 \(k\) 范围以内。如果能跳到则能比较大小关系,否则答案为 \(0\)。

对于跳的过程我们可以倍增优化。

#define N 200005
int n, m, k, u[N], w[N];
#define L a[x].l
#define R a[x].r
#define ls (x << 1)
#define rs (ls | 1)
#define S a[x].val
#define T a[x].tag
struct seg{
	struct node{int l, r, val, tag;}a[N << 2];
	void pushup(int x,int val){T += val, S += val;}
	void down(int x){if(T)pushup(ls, T), pushup(rs, T), T = 0;}
	void build(int x,int l,int r){
		L = l, R = r, S = inf;
		if(l == r)return; 
		int mid = (l + r) >> 1; 
		build(ls, l, mid), build(rs, mid + 1, r);
	}
	void add(int x,int l,int r,int val){
		if(l > r)return;
		if(L >= l && R <= r)pushup(x, val);
		else{
			down(x); int mid = (L + R) >> 1;
			if(mid >= l)add(ls, l, r, val);
			if(mid < r)add(rs, l, r, val);
			S = min(a[ls].val, a[rs].val);
		}
	}
	void ins(int x,int pos,int op){
		if(L == R)S += op;
		else{
			down(x); int mid = (L + R) >> 1;
			if(mid >= pos)ins(ls, pos, op); else ins(rs, pos, op);
			S = min(a[ls].val, a[rs].val);
		}
	}
	int ask(int x){if(L == R)return L; down(x); return a[ls].val == 0 ? ask(ls) : ask(rs);}
	void calc(int x,int op){
		ins(1, x, -op * inf); 
		if(x + k <= n)add(1, x + 1, x + k - 1, op);
		else add(1, x + 1, n - 1, op), add(1, 0, k - 1 - (n - x), op);
	}
}c;
struct Seg{
	struct node{int l, r, val, tag;}a[N << 2];
	void pushup(int x,int val){T += val, S += val;}
	void down(int x){if(T)pushup(ls, T), pushup(rs, T), T = 0;}
	void build(int x,int l,int r){
		L = l, R = r;
		if(l == r)S = u[l];
		else{
			int mid = (l + r) >> 1; 
			build(ls, l, mid), build(rs, mid + 1, r);
			S = min(a[ls].val, a[rs].val);
		}
	}
	void add(int x,int l,int r,int val){
		if(L >= l && R <= r)pushup(x, val);
		else{
			down(x); int mid = (L + R) >> 1;
			if(mid >= l)add(ls, l, r, val);
			if(mid < r)add(rs, l, r, val);
			S = min(a[ls].val, a[rs].val);
		}
	}
	void del(int x){
		if(x >= k - 1)add(1, x - k + 1, x, -1);
		else add(1, 0, x, -1), add(1, n - (k - 1 - x), n - 1, -1);
	}
	void maintain(int x){
		if(S)return;
		if(L == R)c.calc(L, 1), S = inf;
		else down(x), maintain(ls), maintain(rs), S = min(a[ls].val, a[rs].val);
	}
}a;
int f[2][N][18], g[2][N][18], t; set<Pr>s;

bool ckf(int x,int y){
	int l = y < x ? x - y : x - y + n, p = 0;
	int r = y > x ? y - x : y - x + n, q = 0;
	pre(i, t, 0){
		if(p + f[0][(x - p + n) % n][i] <= l - k) p += f[0][(x - p + n) % n][i];
		if(q + f[1][(x + q) % n][i] <= r - k)q += f[1][(x + q) % n][i];
	}
	if(p + k <= l)p += f[0][(x - p + n) % n][0];
	if(q + k <= r)q += f[1][(x + q) % n][0];
	if(p + k > l && w[(x - p + n) % n] < w[y])return true;
	if(q + k > r && w[(x + q) % n] < w[y])return true;
	return false;
}
bool ckg(int x,int y){
	int l = y < x ? x - y : x - y + n, p = 0;
	int r = y > x ? y - x : y - x + n, q = 0;
	pre(i, t, 0){
		if(p + g[0][(x - p + n) % n][i] <= l - k) p += g[0][(x - p + n) % n][i];
		if(q + g[1][(x + q) % n][i] <= r - k)q += g[1][(x + q) % n][i];
	}
	if(p + k <= l)p += g[0][(x - p + n) % n][0];
	if(q + k <= r)q += g[1][(x + q) % n][0];
	if(p + k > l && w[(x - p + n) % n] > w[y])return true;
	if(q + k > r && w[(x + q) % n] > w[y])return true;
	return false;
}
int compare_plants(int x,int y){
	if(ckf(x, y))return -1;
	if(ckg(x, y))return 1;
	return 0;
}
void init(int K, vector<int> r){
	k = K, n = si(r);
	rep(i, 0, n - 1)u[i] = r[i];
	a.build(1, 0, n - 1), c.build(1, 0, n - 1);
	pre(i, n - 1, 0){
		a.maintain(1); int x = c.ask(1);
		c.calc(x, -1), a.del(x), w[x] = i;
	}
	rp(i, n + n){
		if(i < k)s.insert(mp(w[i % n], i));
		else{
			auto cur = s.upper_bound(mp(w[i % n], 0));
			if(cur == s.end())f[0][i % n][0] = 0; else f[0][i % n][0] = i - (*cur).se;
			if(cur == s.begin())g[0][i % n][0] = 0; else g[0][i % n][0] = i - (*--cur).se;
			s.insert(mp(w[i % n], i)), s.erase(mp(w[(i - k + 1) % n], i - k + 1));
		}
	}s.clear();
	pr(i, n + n){
		if(i + k > n + n + 1)s.insert(mp(w[i % n], i));
		else{
			auto cur = s.upper_bound(mp(w[i % n], 0));
			if(cur == s.end())f[1][i % n][0] = 0; else f[1][i % n][0] = (*cur).se - i;
			if(cur == s.begin())g[1][i % n][0] = 0; else g[1][i % n][0] = (*--cur).se - i;
			s.insert(mp(w[i % n], i)), s.erase(mp(w[(i + k - 1) % n], i + k - 1));
		}
	}
	t = log2(n);
	rp(j, t)rep(i, 0, n - 1)
		f[0][i][j] = min(inf, f[0][i][j - 1] + f[0][(i - f[0][i][j - 1] % n + n) % n][j - 1]), 
		g[0][i][j] = min(inf, g[0][i][j - 1] + g[0][(i - g[0][i][j - 1] % n + n) % n][j - 1]), 
		f[1][i][j] = min(inf, f[1][i][j - 1] + f[1][(i + f[1][i][j - 1]) % n][j - 1]), 
		g[1][i][j] = min(inf, g[1][i][j - 1] + g[1][(i + g[1][i][j - 1]) % n][j - 1]);
}

T2:[IOI2020]连接擎天树

较为简单的构造,显然满足条件的只有森林和基环树森林,简单讨论一下即可构造。

#include<vector>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 1005
using std::vector;
typedef vector<vector<int> > vv;
int get(int *u,int x){return u[x]==x?x:u[x]=get(u,u[x]);}
vector<int>c[N];
void build(vector<vector<int> > b);
int construct(vv p){
	vv e;int n=p.size(),fa[N],ga[N];
	e.resize(n);rep(i,0,n)fa[i]=ga[i]=i;
	rep(i,0,n-1)e[i].resize(n);
	rep(i,0,n-1)rep(j,i+1,n-1)if(p[i][j]==3)return 0;
	rep(i,0,n-1)rep(j,i+1,n-1)if(p[i][j]==1)fa[get(fa,i+1)]=get(fa,j+1);
	rep(i,0,n-1)rep(j,i+1,n-1)if(p[i][j]==2){
		int l=get(fa,i+1),r=get(fa,j+1);
		if(l == r)return 0;
		ga[get(ga,l)] = get(ga,r);
	}
	rep(i,0,n-1)rep(j,i+1,n-1)
		if(!p[i][j]&&(get(fa,i+1)==get(fa,j+1)||get(ga,get(fa,i+1))==get(ga,get(fa,j+1))))return 0;
	rep(i,1,n)if(fa[i]!=i)e[i-1][get(fa,i)-1]=e[get(fa,i)-1][i-1]=1;else c[get(ga,i)].push_back(i-1);
	rep(i,1,n)if(c[i].size()>1){
		if(c[i].size()==2)return 0;
		int m=c[i].size();
		rep(j,0,m-2)e[c[i][j]][c[i][j+1]]=e[c[i][j+1]][c[i][j]]=1;
		e[c[i][0]][c[i][m-1]]=e[c[i][m-1]][c[i][0]]=1;
	}
	build(e);return 1;
}

T3:[IOI2020]嘉年华奖券

有一定难度的贪心题。

先考虑 Sub1,对于商家的策略,一定是选择中位数最优。所以等价于将 \(n\) 个数,一半取负号,一半取正号的最优值,直接排序即可。

再考虑 Sub2,每行选择一个数,一半取正一半取负的最优值。如果取正则一定是这一行的最大值,否则是最小值。先每一行都取负,然后选择一半取正,直接按最大值和最小值的和排序即可。

对于 Sub3,就是考虑 0/1 匹配方案使得 \(0\) 和 \(1\) 的匹配尽量多。直接贪心,每次选择 \(1\) 最多的 \(\dfrac{n}{2}\) 行选 \(1\) 取正,剩下的行选 \(0\) 取负。

这启发了 Sub4 的解法,如果所有数都要选,那么对于最大的 \(\dfrac{nm}{2}\) 个数取正,剩下的取负,然后问题就变成了 \(0/1\) 匹配,将所有行按所剩正数个数排序,每一轮前面一半的正数和后面一半的负数匹配即可构造出最优解。因为每一轮 \(1\) 的个数都是 \(size\) 的一半,所以不存在 \(>\dfrac{n}{2}\) 行每行都是 \(1\)。

前面子任务的分析已经将问题解决了,对于所有数据,我们先按 Sub2 的贪心,选出 \(\dfrac{nk}{2}\) 个数使得较大的一半减去较小的一半最大,然后用 Sub4 的方法构造方案。

时间复杂度 \(\mathcal{O}(nm\log n)\)。

#define N 1505
int n, m, k, p[N], u[N][N]; Pr a[N][N]; LL ans = 0;
priority_queue<Pr>q; Pr c[N];
LL find_maximum(int K, std::vector<std::vector<int>> w){
	k = K, n = si(w), m = si(w[0]);
	//read(n, m, k);
	rp(i, n){
		p[i] = 0;
		rp(j, m)a[i][j].fi = w[i - 1][j - 1], a[i][j].se = j;
		sort(a[i] + 1, a[i] + m + 1);
		rp(j, k)ans -= a[i][j].fi;
		q.push(mp(a[i][m].fi + a[i][k].fi, i));
	}
	pr(i, n * k / 2){
		ans += q.top().fi; int x = q.top().se; q.pop();
		p[x]++; 
		if(p[x] < k)q.push(mp(a[x][m - p[x]].fi + a[x][k - p[x]].fi, x));
	}
	memset(u, ~0, sizeof(u));
	pr(id, k){
		rp(i, n)c[i] = mp(p[i], i);
		sort(c + 1, c + n + 1);
		rp(i, n / 2){
			int x = c[i].se;
			u[x][a[x][id - p[x]].se] = id - 1;
			x = c[n - i + 1].se;
			u[x][a[x][m - (--p[x])].se] = id - 1;
		}
	}
	std::vector<std::vector<int>> s(n);
	rep(i, 0, n - 1)s[i].resize(m);
	rp(i, n)rp(j, m)s[i - 1][j - 1] = u[i][j];
	allocate_tickets(s);
	return ans;
}

标签:总结,return,val,get,int,rep,Day1,IOI2020,ls
来源: https://www.cnblogs.com/7KByte/p/16035419.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有