Loading

【总结】「ROIR 2020」

比较简单的比赛。

T1

直接平方差得到 \((x+y)(x-y) = n\)

所以我们将 \(n\) 拆成两个数 \(a,b\) 的乘积,然后 \(x=\frac{a+b}{2},y=\frac{a-b}{2}\)

如果 \(n\) 为奇数,直接拆成 \(n\times 1\)

如果 \(n\) 为偶数,且 \(\frac{n}{2}\) 也是偶数,就拆成 \(\frac{n}{2}\times 2\),否则无解。

int main() {
	//int T = read();while(T--)solve();
	LL n = Read();
	if(n == 0)cout << "YES\n1 1\n";
	else if(n == 1 || n == 4)cout << "NO\n";
	else if(n & 1)cout << "YES\n" << (n + 1) / 2 << " " << (n - 1) / 2 << "\n";
	else{
		if((n / 2) & 1)cout << "NO\n";
		else {
			LL w = n / 2;
			cout << "YES\n" << (w + 2) / 2 << " " << (w - 2) / 2 << "\n";
		}
	}
	return 0;
}

T2

\(n\)\(m\) 大,显然二分 \(v\) 的值即可。时间复杂度 \(\mathcal{O}(qn\log m)\)

#define N 200005
int n, m, q, v[11], len[11], a[N], f[N];
int main(){
	scanf("%d", &n);
	rep(i, 1, n)scanf("%d", &v[i]);
	rep(i, 1, n)scanf("%d", &len[i]);
	scanf("%d", &m);
	rep(i, 1, m - 1)scanf("%d", &a[i]);
	rep(i, 1, m)scanf("%d", &f[i]);
	scanf("%d", &q);
	while(q--){
		int x, y;
		scanf("%d%d", &y, &x);
		x -= y;
		int l = 0, r = m - 1, ed = m;
		while(l <= r){
			int mid = (l + r) >> 1;
			double sum = 0;
			rep(i, 1, n)sum += 1.00 * len[i] / (v[i] + a[mid]);
			if(sum <= x)ed = mid, r = mid - 1;
			else l = mid + 1;
		}
		cout << f[ed] << endl;
	}
	return 0;
}

T3

对每种相同颜色算贡献。

对于两个同色位置 \(i,j\),如果不存在 \(i<k<j\) 使得 \(k\) 与之颜色相同,那么所有包含 \((i,j)\) 的区间的答案都要 \(-1\)

由于有 \(\mathcal{O}(N^2)\) 个区间,但是我们只用统一计算长度相同的区间,所以直接差分即可。线性复杂度。

#define N 200005
int n, a[N], b[N], c[N];LL p[N], sz[N], w[N];
int main(){
	scanf("%d", &n);
	rep(i, 1, n)scanf("%d", &a[i]), b[i] = a[i];
	sort(b + 1, b + n + 1);
	int T = unique(b + 1, b + n + 1) - b - 1;
	rep(i, 1, n)a[i] = lower_bound(b + 1, b + T + 1, a[i]) - b;
	auto ins = [=](int l,int r){
		if(l > n - r + 1)swap(l, r), l = n - l + 1, r = n - r + 1;
		int p1 = r - l + 1, p2 = n - l + 1;
		sz[p1] ++, w[p1] += p1 - 1, sz[r] --, w[r] -= p1 - 1;
		w[r] -= l, w[p2] += l, p[p2] ++;
	};
	rep(i, 1, n){
		if(c[a[i]])ins(c[a[i]], i);
		c[a[i]] = i;
	}
	rep(i, 1, n){
		w[i] += w[i - 1], sz[i] += sz[i - 1], p[i] += p[i - 1];
		printf("%lld ", (i - p[i]) * (n - i + 1) + w[i] - sz[i] * i);
	}
	return 0;
}

T4

题面看起来比较繁琐,但也不是不可做。

根据题意每一行都是一颗树,且任意非叶子节点都有两个儿子,所以我们先把表达式树建出来。

首先我们先把每个位置都填上 \(0\),显然每一行答案都是 \(0\)。接着我们依次将一个 \(0\) 变成 \(1\),由于每变动一次,答案最多只会 \(+1\),所以一定会经过 \(s\)

所以我们在表达式树上均摊搞一搞即可,时间复杂度 \(\mathcal{O}(NM)\)

#define N 300005
#define pb push_back
int n, m, s, z[N];
vector<int>f[N], op[N], mat[N], c[N];
int read(){
	int x = 0; char ch = getchar();
	while(ch > '9' || ch < '0')ch = getchar();
	while(ch <= '9'  && ch >= '0')x = (x << 1) + (x << 3) + (ch - '0'), ch = getchar();
	return x;
}
int main(){
	n = read(), m = read(), s = read();
	int sum = 0;
	rep(i, 0, m - 1){
		rep(j, 1, n)f[i].pb(0), op[i].pb(0), c[i].pb(0);
		rep(j, 1, n - 1){
			int x = read() - 1, y = read() - 1, z = read() - 1;
			f[i].pb(0), op[i].pb(z), c[i].pb(0);
			f[i][x] = f[i][y] = j + n - 1;
			sum += z;
		}
	}
	rep(i, 0, n - 1)mat[i].resize(m);
	rep(i, 0, m - 1)rep(j, 0, n - 1)mat[j][read()] = i;
	auto del = [](int x,int y){
		bool fg = y == 1;
		while(y != n + n - 2){
			c[x][y] = 1;
			if(c[x][f[x][y]] == 1)break;
			if(op[x][f[x][y]] == 1)y = f[x][y];
			else{
				if(c[x][f[x][y]] == 0){c[x][f[x][y]] = ~0;break;}
				else y = f[x][y];
			}
		}
		if(y == n + n - 2)c[x][y] = 1, s --;
	};
	if(s){
		rep(i, 0, n - 1){
			rep(j, 0, m - 1){
				z[i]++, del(mat[i][j], i);
				if(!s)break;
			}
			if(!s)break;
		}
	}
	rep(i, 0, n - 1)printf("%d ", z[i]);
	return 0;
}

T5

根据均值不等式,左右差最小时乘积最大,线性扫一遍即可。

int n;long long a[N];
int main(){
	n = read();
	rep(i, 1, n)a[i] = read() + a[i - 1];
	auto w = [](int x){return abs(a[x] - (a[n] - a[x]));};
	int ed = 1;
	rep(j, 2, n - 1)if(w(j) < w(ed))ed = j;
	cout << ed << endl;
	return 0;
}

T6

很神奇一数学题。

由于 \(ab-(a-1)(b-1) = a+b-1=n\),所以 \(a+b \le n + 1\),每个数的范围都是 \(3000\) 之内。

如果直接枚举 \(a,b\),再枚举一个 \(c\) 就能算出来,本以为是 \(\mathcal{O}(N^3)\),没想到剪枝后跑的飞快。

但还是写了个奇怪的东西,我们枚举 \(a,c\),然后扩欧解二元一次方程 \(ab-cd=n\) 即可。

int n, x;
int exgcd(int a,int b,int &x,int &y){
	if(!b){x = 1, y = 0; return a;}
	int xx = 0, yy = 0;
	int g = exgcd(b, a % b, xx, yy);
	x = yy, y = xx - a / b * yy; return g;
}
int main(){
	scanf("%d%d", &n, &x);
	int ans = 0;
	rep(a, 2, n)if(a != x)rep(c, 1, a - 1){
		int b = 0, d = 0;
		int g = exgcd(a, c, b, d);
		if(n % g)continue;
		int k = n / g, lb = c / g, ld = a / g;
		b *= k, b = (b % lb + lb) % lb;
		d = (n - a * b) / c;
		while(b > -d){
			if(d < 0 && b != x)ans++;
			b += lb, d -= ld;
		}
	}
	cout << ans << endl;
	return 0;
}

T7

先考虑没有 \(b\) 的限制怎么做。

显然我们可以从小到大递推,\(f_i\) 表示 \(<a_i\) 的数中答案最大的数,\(g_i\) 表示对应的答案,一遍递推即可。

如果有 \(b\),我们二分出小于等于 \(b\) 的最大的 \(a_i\),先加上 \(f_i\),剩下的加 \(a_i\)

#define N 200005
int n, m;LL a[N], u[N], v[N];
int main(){
	scanf("%d", &n);
	rep(i, 1, n)scanf("%lld", &a[i]);
	rep(i, 1, n - 1){
		LL p = (a[i + 1] - 1 - u[i]) / a[i];
		u[i + 1] = u[i] + p * a[i], v[i + 1] = v[i] + p;
	}
	scanf("%d", &m);
	while(m--){
		LL s; scanf("%lld", &s);
		int p = upper_bound(a + 1, a + n + 1, s) - a - 1;
		LL w = (s - u[p]) / a[p];
		printf("%lld %lld\n", u[p] + w * a[p], w + v[p]);
	}
	return 0;
}

T8

线段树,状态用 \(f_{l,r}\) 表示最左边选了 \(l\) 个,最右边选了 \(r\) 个,然后合并即可。

#define N 40005
int n, u[N];
inline void cmx(LL &x,LL y){if(x < y)x = y;}
struct node{
	LL a[4][4]; int len;
	node(){memset(a, 0xcf, sizeof(a));}
	node operator+(node o){
		node cur; cur.len = len + o.len;
		rep(l, 0, 3)rep(r, 0, 3)rep(p, 0, 3)rep(q, 0, 3 - p){
			int x = l + (l == len) * q, y = r + (r == o.len) * p;
			if(x <= 3 && y <= 3)cmx(cur.a[x][y], a[l][p] + o.a[q][r]);
		}
		return cur;
	}
};
struct Node{
	int l, r; node sum;
}a[N << 2];
#define L a[x].l
#define R a[x].r
#define ls (x << 1)
#define rs (ls | 1)
#define S a[x].sum
void build(int x,int l,int r){
	L = l, R = r;
	if(l == r){
		S.len = 1;
		S.a[0][0] = 0, S.a[1][1] = u[l];
		return ;
	}
	int mid = (l + r) >> 1;
	build(ls, l, mid), build(rs, mid + 1, r);
	S = a[ls].sum + a[rs].sum;
}
void ins(int x,int pos,int val){
	if(L == R)S.a[1][1] = val;
	else{
		int mid = (L + R) >> 1;
		if(mid >= pos)ins(ls, pos, val);
		else ins(rs, pos, val);
		S = a[ls].sum + a[rs].sum;
	}
}
int main(){
	scanf("%d", &n);
	rep(i, 1, n)scanf("%d", &u[i]);
	auto out = [](){
		LL ans = 0;
		rep(l, 0, 3)rep(r, 0, 3 - l)cmx(ans, a[1].sum.a[l][r]);
		printf("%lld\n", ans);
	};
	int q;scanf("%d", &q);
	build(1, 1, n), out();
	while(q--){
		int x, y;
		scanf("%d%d", &x, &y);
		ins(1, x, y), out();
	}
	return 0;
}
posted @ 2021-11-17 16:10  7KByte  阅读(303)  评论(0编辑  收藏  举报