20210716模拟赛

是 zzq 神仙出的题。

而且看到了 zzq 神仙本人啦!

T1

奇妙的构造,有些眼熟但应该没见过。那一道是环上交换前缀和的毒瘤题,不会。

这个一开始想分成若干段,长度为 20 ,然后每一段分别使用最优解法,发现答案并不是很优,只有 30pts 的样子。

考虑如果这段的开头是 0 ,那么把整段后移,不会更劣,似乎确实优了一点点,大概能有个 30 多分。

想把 20 变成更大一点的数,在时间复杂度允许的情况下大概是不到 40 分。

然后乱搞,对于每一个 1 ,向后枚举 100 个位置,看是否出现了符合要求的三个 1 ,然后把它们一起删掉。

发现优化的非常明显,可以直接分数拿满。

zzq说我们吊打了 IOI AKer和国家队(

正解就是找当前这个 1 有没有办法给他和另外两个一起删掉,否则使用奇怪的方法把它删掉。

T2

神仙题,仅对于我来说,毕竟我菜。

首先想到一定是树形结构,组成了一个森林,如果使用 LCT 没准不错可惜我不会 LCT。朴素想法使用 \(O(n^3)\) 暴力,可惜没有分。思考平方做法,或者带log。我们每次将一个点改变父亲的时候,把它的儿子的 fa 倍增数组给重构出来,可以在 \(O(n^2logn)\) 的时间复杂度内修改,在 \(O(n^2logn)\) 的复杂度内查询。然后发现我干嘛构建倍增数组,直接维护根就完了。时间复杂度 \(O(n^2)\)

考虑如何解决随机数据。可以发现修改的时间消耗大概不会很多,期望应该是 \(O(logn)\) 重构一次。看怎么处理询问。给每个点维护出应该的颜色,然后使用分块维护一下,可以 \(\sqrt{n}\) 进行询问。

正解也有分块。离线做法,将询问分块,发现每块内父亲改变的点只有 \(\sqrt{n}\) 个,维护这些点,修改的时候暴力重构即可,复杂度是单次 \(O(\sqrt{n})\) 。考虑询问。我们认为这些特殊点有各自的颜色,这个可以维护。找被一个特殊点统帅的点有哪些,对于一个特殊点,分块维护每一块里的被统帅点个数,然后前缀和预处理一下。查询的时候对于整块,枚举特殊点查询,对于散点,查看他的被哪个特殊点统帅,计入贡献。对于那些不被统帅的非特殊点。随便用个数据结构比如说树状数组维护一下就好了。最终复杂度可以 \(O(n\sqrt{n})\)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>

using namespace std;

int read()
{
	int a = 0,x = 1;char ch = getchar();
	while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();}
	return a*x;
}
const int N=2e5+7;
int n,fa[N],a[N],q,b,head[N],go[N],nxt[N],cnt,anc[N];
struct node{int op,x,y;}arr[N];
void add(int u,int v)
{
	go[++cnt] = v;
	nxt[cnt] = head[u];
	head[u] = cnt;
}
int _fa[N],_head[N],_go[N],_nxt[N],_cnt,_a[N],s[N][400],vis[N],col[N];
vector<int>g;
void _add(int u,int v)
{
	_go[++_cnt] = v;
	_nxt[_cnt] = _head[u];
	_head[u] = _cnt;
}
void dfs(int u,int h)
{
	int ret = 1;
	for(int e = head[u];e;e = nxt[e]) {
		int v = go[e];a[v] = a[u];
		dfs(v,vis[u] ? u : h);
	}
	_fa[u] = h;if(vis[u]) h = u,ret = 0,_a[u] = a[u],g.push_back(u);anc[u] = h;
	if(anc[u] == u && _fa[u]) _add(_fa[u],u);
	s[h][col[u]] ++;
}

void update(int u)
{
	for(int e = _head[u];e;e = _nxt[e]) {
		int v = _go[e];if(_fa[v] != u || _a[v] == _a[u]) continue;
		_a[v] = _a[u];update(v);
	}
}
int S[N];
void Add(int p,int x)
{
	for(int i = p;i <= n;i += i&-i) S[i] += x;
}
int query(int p) 
{
	int ret = 0;
	for(int i = p;i >= 1;i -= i&-i) ret += S[i];
	return ret;
} 
int query(int l,int r)
{
	int ret = 0;
	if(col[r] > col[l]) for(auto o : g) {
		ret += (s[o][col[r]-1] - s[o][col[l]])*_a[o];
	}
	for(int i = l;i <= r && col[l] == col[i];i ++) {
		ret += anc[i] ? _a[anc[i]] : 0;
	}
	if(col[l] != col[r]) for(int i = r;i >= l && col[i] == col[r];i --) {
		ret += _a[anc[i]];
	}
	return ret + query(r) - query(l-1);
}
void solve(int l,int r)
{
	for(int i = l;i <= r;i ++) {
		if(arr[i].op <= 2) vis[arr[i].x] = 1;
	}
	for(int i = 1;i <= n;i ++) head[i] = _head[i] = S[i] = 0;_cnt = cnt = 0;
	for(int i = 1;i <= n;i ++) {
		if(fa[i] != i) add(fa[i],i);
	}g.clear();
	for(int i = 1;i <= n;i ++) {
		if(fa[i] == i) {dfs(i,0);}
	}
	for(auto o : g) {
		for(int i = 1;i <= col[n];i ++) s[o][i] += s[o][i-1];
	}
	for(int i = 1;i <= n;i ++) {
		if(anc[i] == 0) Add(i,a[i]);
	}
	for(int i = l;i <= r;i ++) {
		if(arr[i].op == 1) {
			_fa[arr[i].x] = arr[i].x;_a[arr[i].x] = arr[i].y;
			update(arr[i].x);
		} else if(arr[i].op == 2) {
			_fa[arr[i].x] = anc[arr[i].y],_a[arr[i].x] = anc[arr[i].y] ? _a[anc[arr[i].y]] : a[arr[i].y];
			if(_fa[arr[i].x]) _add(_fa[arr[i].x],arr[i].x);
			update(arr[i].x);
		} else {
			printf("%d\n",query(arr[i].x,arr[i].y)); 
		} 
	}
	for(auto o : g) {
		for(int i = 1;i <= col[n];i ++) s[o][i] = 0;
	}
	for(int i = l;i <= r;i ++) {
		if(arr[i].op <= 2) vis[arr[i].x] = 0;
	}
}

int main()
{
	freopen("cells.in","r",stdin);freopen("cells.out","w",stdout);
	n = read(),q = read();b = n;
	for(int i = 1;i <= n;i ++) a[i] = read(),fa[i] = i,col[i] = (i-1)/b+1;
	for(int i = 1;i <= q;i ++) {
		arr[i].op = read(),arr[i].x = read(),arr[i].y = read();
	}
	for(int i = 1;i <= q;i ++) {
		if(i%b == 1) solve(i,min(q,i+b-1));
		if(arr[i].op == 1) a[arr[i].x] = arr[i].y,fa[arr[i].x] = arr[i].x;
		else if(arr[i].op == 2) fa[arr[i].x] = arr[i].y;
	}
} 

T3

神仙构造,并不会做。

对于 \(m = 1\) 的时候,只有 \(n = 1\) 时有解。

对于每一个铜丝全都小于等于 2 的,把长度为 2 的贴边放剩下的空位填 1 就好了。

不小于 2 的,把所有大于 2 的左端弯折一下,然后剩下的空位用 2 补齐。长度为 n 的铜丝需要 n-3 个 2。

忘记输出 yes 少了 40pts 的痛……

posted @ 2021-07-16 20:55  nao-nao  阅读(56)  评论(0编辑  收藏  举报