CSP-S模拟10

又双叒叕模拟退役了。。。。。。。

话说快读打挂了,为啥在本地能跑? 甚至切了 \(T3\) \(T1\) (赛后)

有会的大佬解释下吗

upd:solved,感谢Muel_imj大佬

读到的第一个字符是数字,所以第一个数直接到第二个 while 不会出错。快读在最后时会多读一个字符,恰好把数字之间的恰好一个空格/回车读掉了,再次开始又会直接到第二个 while 如此往复。但凡多一个空格就死掉了。

错误快读

while(c < '0' || c > '9')x = getchar();

正确写法

while(c < '0' || c > '9')c = getchar();

完整版

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}

A. 欧几里得的噩梦

我不会线性基,但是这题可以找规律得出正解

赛时挂了是因为只看到了字典序最小,没注意前面还有个限制条件,把输出改一下(三行),即可

我是什么 \(sb\)

通过一系列的找规律,手动枚举,最终得出结论

对二进制并查集,新开一个 \(m + 1\)

每次插入一个 \(1\) 合并该位和 \(m + 1\)

插入 \(2\) 合并两位

每次成功插入答案乘 \(2\)

成功插入的元素记下来输出即可

你要问我这为啥是对的,我也不知道,反正就是找规律猜结论

正确理解等我学完线性基再说

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}

const int maxn = 500005;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int n, m;
int ans, las, f[maxn];
vector<int>v;
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
int main(){
	n = read(); m = read();
	for(int i = 1; i <= m + 1; ++i)f[i] = i;
	ans = 1;
	for(int i = 1; i <= n; ++i){
		int o = read();
		if(o == 1){
			int p = read(); p = fa(p);
			if(p != m + 1){
				f[p] = m + 1;
				ans += ans; ans %= mod; v.push_back(i); continue;
			}			
		}else{
			int p = read(), q = read();
			p = fa(p); q = fa(q);
			if(p == q)continue;
			ans += ans; ans %= mod; v.push_back(i);
			f[min(p, q)] = max(p, q);
		}
	}
	printf("%d %d\n",ans, v.size());
	for(int i : v)printf("%d ",i);
	return 0;
}

B. 清扫

简单树形 \(dp\) ,甚至只有一个 \(dfs\)

为啥赛时挂了呢。。。。。我为啥还是这么菜

考虑一个点的子树内叶子对该点有影响,要么是他的不同子节点内叶子配对个数记为 \(x\) ,要么是从他这里上去,记为 \(y\)

\(x + y = sum\)

\(x + y / 2 = a[x]\)

\(sum\) 为他的儿子们上传的需要配对的数量和

解这个方程判断是否合法,

注意能够配对的点数在存在主元素时有上界

最后记得看一下根是否为 \(0\)

对了,根不能是叶子

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}

const int maxn = 100005;
const int inf = 0x3f3f3f3f;
int n, a[maxn], f[maxn];
struct edge{
	int to, net;
}e[maxn << 1 | 1];
int head[maxn], tot;
void add(int u, int v){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
}
bool dfs(int x, int fa){
	ll sum = 0, mx = 0; bool leaf = 1;
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa)continue;
		leaf = 0;
		if(dfs(v, x) == false)return false;
		sum += a[v]; mx = max(mx, (ll)a[v]);
	}
	if(leaf)return true;
	ll y = 2 * (sum - a[x]), now = sum - y;
	ll pr = (sum - (mx > sum / 2 ? mx + mx - sum : 0)) / 2;
	if(y > pr + pr || y < 0 || sum < a[x])return false;
	a[x] = now;
	return true;
}
int rd[maxn];
int main(){
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		add(u, v); add(v, u);
		++rd[u]; ++rd[v];
	}
	int root = -1;
	for(int i = 1; i <= n; ++i)if(rd[i] > 1)root = i;
	if((root == -1 && a[1] == a[2]) || (root != -1 && dfs(root, 0) && a[root] == 0))printf("YES\n");
	else printf("NO\n");
	return 0;
}

C. 购物

如果存在数 \(x\) ,那么 \([(x + 1)/2, x]\) 均合法

如果 \((x + 1) / 2 \leq y \leq x\)

那么 \((x + 1 + z) / 2 \leq y + z \leq x +z\)

也就是说,当前两个元素管控区域相连,那么加入一些数后仍相连

于是我们就能记录二元组 \(x ,y\) 分别表示一串相连的元素的最大值与最小值

每次加入一个数与所有二元组组成一堆新的二元组与原来的二元组归并,然后合并相连部分

这个二元组的个数是 \(log\sum a_i\) 级别的,因为没个二元组至少砍掉了当前值域的一半

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}

const int maxn = 100005;
const int inf = 0x3f3f3f3f;
int n, a[maxn];
struct node{
	ll x, y;
}tmp[maxn], ans[maxn], t[maxn];

int main(){
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	int top = 0;
	for(int i = 1; i <= n; ++i){
		int p = 0;
		for(int j = 1; j <= top; ++j)tmp[++p] = {ans[j].x + a[i], ans[j].y + a[i]};
		tmp[++p] = {a[i], a[i]};
		int tp = 0, p1 = 1, p2 = 1;
		while(p1 <= top && p2 <= p){
			if(p1 <= top && ans[p1].y > tmp[p2].y){
				t[++tp] = ans[p1++];				
			}else{
				t[++tp] = tmp[p2++];
			}
		}
		while(p1 <= top)t[++tp] = ans[p1++];				
		while(p2 <= p)t[++tp] = tmp[p2++];
		// for(int j = 1; j <= tp; ++j)printf("%lld %lld ", t[j].x, t[j].y); printf("\n");
		top = 0; ans[++top] = t[1];
		for(int j = 2; j <= tp; ++j){
			if((ans[top].x + 1) / 2 <= t[j].y)ans[top].x = min(ans[top].x, t[j].x);
			else ans[++top] = t[j];
		}
	}
		// for(int j = 1; j <= top; ++j)printf("%lld %lld\n", ans[j].x, ans[j].y);
	ll g = 0;
	for(int i = 1; i <= top; ++i){
		g += ans[i].y - (ans[i].x - 1) / 2;
		// if(ans[i - 1].y >= (ans[i].x + 1) / 2)ans[i - 1].y =( ans[i].x - 1) / 2;
	}
	printf("%lld\n",g);
	return 0;
}

D. ants

原题我没看出来。,。。、、

回滚莫队,就当复习了

记录 \(len, ren\) 表示在值域上该数向下、向上扩展的连续段最大长度,你发现只需要维护端点的信息即可

回滚可以开栈记录所有操作,退栈即可

code
#include<bits/stdc++.h>

using namespace std;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}

const int maxn = 1000005;
int n, m, block, bl[maxn], a[maxn];
int len[maxn], ren[maxn], ans[maxn];
struct stac{int pos, val;}sta[maxn];
struct query{
	int l, r, id;
	friend bool operator < (const query &x, const query &y){
		return bl[x.l] == bl[y.l] ? x.r < y.r : x.l < y.l;
	}
}q[maxn];

void solve(){
	int l = 0, r = 0, nans = 0;
	for(int i = 1; i <= m; ++i){
		if(bl[q[i].l] != bl[q[i - 1].l]){
			for(int j = 1; j <= n; ++j)len[j] = 0;
			for(int j = 1; j <= n; ++j)ren[j] = 0;
			l = r = bl[q[i].l] * block; nans = 0;
		}
		while(r < q[i].r){
			++r;
			len[a[r]] = len[a[r] - 1] + 1;
			ren[a[r]] = ren[a[r] + 1] + 1;
			int now = len[a[r]] + ren[a[r]] - 1;
			len[a[r] + ren[a[r]] - 1] = now;
			ren[a[r] - len[a[r]] + 1] = now;
			nans = max(nans, now);
		}
		int tmp = nans, top = 0, up = min(q[i].r, l);
		for(int p = q[i].l; p <= up; ++p){
			len[a[p]] = len[a[p] - 1] + 1;
			ren[a[p]] = ren[a[p] + 1] + 1;
			int now = ren[a[p]] + len[a[p]] - 1;
			int pl = a[p] - len[a[p]] + 1, pr = a[p] + ren[a[p]] - 1;
			sta[++top] = {pl, ren[pl]}; sta[++top] = {pr, len[pr]};
			ren[pl] = len[pr] = now;
			tmp = max(tmp, now);
		}
		ans[q[i].id] = tmp;
		for(int p = top; p > 0; --p)
			if(p & 1)ren[sta[p].pos] = sta[p].val;
			else len[sta[p].pos] = sta[p].val;
		for(int p = q[i].l; p <= up; ++p)len[a[p]] = ren[a[p]] = 0;
	}
}

int main(){
	n = read(); m = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= m; ++i)q[i].l = read(), q[i].r = read(), q[i].id = i;
	block = sqrt(n); for(int i = 1; i <= n; ++i)bl[i] = (i + block - 1) / block;
	sort(q + 1, q + m + 1); solve();
	for(int i = 1; i <= m; ++i)printf("%d\n",ans[i]);
	return 0;
}

点踩打卡 \(rp--\)

推荐打卡 \(rp++\)

posted @ 2022-09-24 19:00  Chen_jr  阅读(76)  评论(6编辑  收藏  举报