Loading

【游记】NOI Online 2022

感觉题目不是很难,前两题比较轻松,第三题也不难,大概 10:30 的时候写完检查完就交了。

民间数据发现 T2 输出 printf("%d %d\n", v[y], x) 写成了 printf("%d %d\n", v[y], y) 挂了/ll。

感觉挺活该的,写完后只对比了一下 YES 和 NO 是否正确,甚至没有 check 自己输出是否合法就交了。

不过也是幸运的至少不是在省选/NOI上挂分,至少下次遇到 OI 赛制的 spj 题肯定会手写 checker 验证一下。

T1

一眼题,先从 \(1\to n\) 扫一遍,\(p_i\) 表示扫到 \(i\) 时的栈顶,那么询问 \((l,r)\) 即为区间 \([l,r]\)\(p_i < l\)\(i\) 个数,离线树状数组即可,复杂度 \(\mathcal{O}(N\log N)\)

#define N 500005
int n, m, a[N], b[N], sta[N], top, p[N], ans[N], c[N];
vector<Pr>u[N];
inline void add(int x){for(; x <= n; x += x & -x)c[x]++;}
inline int ask(int x){int sum = 0; for(; x; x -= x & -x)sum += c[x]; return sum;}
int main() {
	//freopen("stack.in","r",stdin);
	//freopen("stack.out","w",stdout);
	read(n, m);
	rp(i, n)read(a[i]);
	rp(i, n)read(b[i]);
	rp(i, n){
		while(top && (a[sta[top]] == a[i] || b[sta[top]] <= b[i]))top--;
		if(top)p[i] = sta[top];
		sta[++top] = i;
	}
	rp(i, m){
		int x, y; read(x, y);
		ans[i] = - x + 1;
		u[y].pb(mp(x - 1, i));
	}
	int s = 0;
	rp(i, n){
		if(!p[i])s++;
		else add(p[i]);
		go(x, u[i])ans[x.se] += ask(x.fi) + s;
	}
	rp(i, m)printf("%d\n", ans[i]);
	return 0;
}

T2

\(size\) 相同的集合放在一起,哈希判重,将完全相同的集合只保留一个。

先考虑 \(size\) 相同的匹配,因为不存在完全相同的集合,所以只要一个位置上有两个集合包括它,那么就是合法匹配。

然后 \(size\) 从大往下扫,维护 \(c_i\) 表示位置 \(i\) 被哪个集合占了。如果扫到当前集合,所有位置的 \(c_i\) 相同,说明被包含直接忽略,否则 \(c_i\) 和当前集合一定是合法匹配。

不知道输入的集合是否有序,如果无序还要排序多一个 log。

#define N 1000005
typedef unsigned long long ull;
int n, v[N], u[N];
unordered_map<ull, int>h;
vector<int>a[N], c[N];
bool calc(int id){
	go(x, c[id]){
		go(y, a[x])if(v[y]){
			printf("YES\n%d %d\n", v[y], x);
			return true;
		}
		go(y, a[x])v[y] = x;
	}
	go(x, c[id]){
		int lst = u[a[x][0]]; bool flag = false;
		go(y, a[x]){
			v[y] = 0;
			if(u[y] != lst)flag = true;
		}
		if(flag){
			puts("YES");
			int p = 0;
			go(y, a[x])if(u[y]){
				if(!p || si(a[p]) > si(a[u[y]]))p = u[y];
			}printf("%d %d\n", p, x); return true;
		}
	}
	go(x, c[id])go(y, a[x])u[y] = x;
	return false;
}
void solve(){
	read(n), h.clear();
	rp(i, n)c[i].clear(), u[i] = v[i] = 0;
	rp(i, n){
		int k; read(k);
		if(!k)continue;
		a[i].clear();
		int lst = 0, flag = 0;
		rp(j, k){
			int x; read(x);
			flag |= x < lst, lst = x;
			a[i].pb(x);
		}
		if(flag)sort(a[i].begin(), a[i].end());
		ull s = 0;
		rp(j, k)s = s * B + (a[i][j - 1]);
		if(h.count(s))continue;
		h[s] = 1, c[k].pb(i);
	}
	pr(i, n)if(si(c[i]))if(calc(i))return;
	puts("NO");
}
int main() {
//	freopen("discuss.in","r",stdin);
//	freopen("discuss.out","w",stdout);
	int T; read(T);
	while(T--)solve();
	return 0;
}

T3

\(m = 2\) 时,\(Ans = \sum a_{i,j} \times (n + n)\)

\(m = 3\) 时,考虑枚举 \(x\),表示 \(a_{x,i} + a_{x,j}\) 作为答案的情况。以 \((a_{y,i} - a_{x,i}, a_{z,i} - a_{x,i})\) 作为二元组,离线二维数点即可。

对于 \(m=4\) 的情况,我们需要求 \(\min + \max\) 而不是单独求 \(\min\)\(\max\) 显然是有性质。

这提示已经非常明显了,我们将 \(\min\) 容斥掉,\(\min\{S_4\} = \max\{S_3\} - \max\{S_2\} + \max\{S_1\} - \max\{S_4\}\),不难发现 \(-\max\{S_4\}\) 和我们要求的 \(\max\) 抵消了,问题转化为 \(m = 3\) 的情况。

时间复杂度 \(\mathcal{O}(2^MN\log N)\)

#define N 200005
int m, n, u[4][N], M = N - 4; LL ans;
struct node{
	int x, y, w;
	bool operator<(const node o)const {
		if(x != o.x)return x < o.x;
		return y < o.y;
	}
}a[N], b[N];
LL c[2][N << 1];
inline void add(int op, int x,int val){for(; x <= M + M; x += x & -x)c[op][x] += val;}
inline LL ask(int op,int x){LL sum = 0; for(; x; x -= x & -x)sum += c[op][x]; return sum;}
void calc(int l,int r){
	rp(i, n)
		b[i] = node{M - a[i].x, M - a[i].y, a[i].w}, a[i].x += M - l, a[i].y += M - r;
	sort(a + 1, a + n + 1), sort(b + 1, b + n + 1);
	int j = 1;
	memset(c, 0, sizeof(c));
	rp(i, n){
		while(j <= n && b[j].x <= a[i].x)add(0, b[j].y, 1), add(1, b[j].y, b[j].w), j++;
		ans += ask(1, a[i].y) + ask(0, a[i].y) * a[i].w;
	}
}
Pr p[N], q[N];
LL solve2(int l,int r){
	rp(i, n)p[i] = mp(u[l][i] - u[r][i], u[l][i]), q[i] = mp(u[r][i] - u[l][i], u[l][i]);
	sort(p + 1, p + n + 1), sort(q + 1, q + n + 1);
	LL cnt = 0, sum = 0, ed = 0; int j = 1;
	rp(i, n){
		while(j <= n && q[j].fi <= p[i].fi)sum += q[j].se, cnt ++, j++;
		ed += cnt * p[i].se + sum;
	}
	rp(i, n)p[i] = mp(u[r][i] - u[l][i], u[r][i]), q[i] = mp(u[l][i] - u[r][i], u[r][i]);
	sort(p + 1, p + n + 1), sort(q + 1, q + n + 1);
	cnt = 0, sum = 0, j = 1;
	rp(i, n){
		while(j <= n && q[j].fi < p[i].fi)sum += q[j].se, cnt ++, j++;
		ed += cnt * p[i].se + sum;
	}
	//cout << "ac " << ed << endl;
	return ed;
}
LL solve1(int x){
	LL w = 0;
	rp(i, n)w += u[x][i] * 1LL * (n + n);
	//cout << "kk " << w << endl;
	return w;
}
void solve3(int x,int y,int z){
	rp(i, n)a[i] = node{u[x][i] - u[y][i], u[x][i] - u[z][i], u[x][i]};
	calc(1, 1);
	rp(i, n)a[i] = node{u[y][i] - u[x][i], u[y][i] - u[z][i], u[y][i]};
	calc(0, 1);
	rp(i, n)a[i] = node{u[z][i] - u[x][i], u[z][i] - u[y][i], u[z][i]};
	calc(0, 0);
}
int main() {
	//freopen("sort.in","r",stdin);
	//freopen("sort.out","w",stdout);
	read(m, n);
	rep(i, 0, m - 1)rp(j, n)read(u[i][j]);
	if(m == 2){
		rp(i, n)ans += (u[0][i] + u[1][i]) * 1LL * (n + n);
		printf("%lld\n", ans);
	}
	else if(m == 3){
		// max
		rp(i, n)a[i] = node{u[0][i] - u[1][i], u[0][i] - u[2][i], u[0][i]};
		calc(1, 1);
		rp(i, n)a[i] = node{u[1][i] - u[0][i], u[1][i] - u[2][i], u[1][i]};
		calc(0, 1);
		rp(i, n)a[i] = node{u[2][i] - u[0][i], u[2][i] - u[1][i], u[2][i]};
		calc(0, 0);
		//cout << "ss " << ans << endl;
		//min
		rp(i, n)a[i] = node{u[1][i] - u[0][i], u[2][i] - u[0][i], u[0][i]};
		calc(1, 1);
		rp(i, n)a[i] = node{u[0][i] - u[1][i], u[2][i] - u[1][i], u[1][i]};
		calc(0, 1);
		rp(i, n)a[i] = node{u[0][i] - u[2][i], u[1][i] - u[2][i], u[2][i]};
		calc(0, 0);
		printf("%lld\n", ans);
	}
	else{
		solve3(0, 1, 2), solve3(0, 1, 3), solve3(0, 2, 3), solve3(1, 2, 3);
		//cout << "ss " << ans << endl;
		rp(r, 3)rep(l, 0, r - 1)ans -= solve2(l, r);
		//cout << "ss " << ans << endl;
		ans += solve1(0) + solve1(1) + solve1(2) + solve1(3);
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2022-03-26 14:25  7KByte  阅读(297)  评论(1编辑  收藏  举报