【赛前复习】 某憨子的T1、T2专训

2021牛客OI赛前集训营-提高组(第六场)

官方题解

旋律的总数

没意思,

点击查看代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int t, n, m, ans;
int qpow(int a, int b) {
	int res = 1;
	while(b) {
		if(b & 1) res = res * a % mod;
		b >>= 1, a = a * a % mod;
	}
	return res;
} 
signed main() {
	freopen("A.in", "r", stdin);
	freopen("A.out", "w", stdout);
	scanf("%lld", &t);
	while(t --) {
		scanf("%lld %lld", &n, &m);
		ans = qpow(m, n - 1);
		printf("%lld\n", ans);
	}
	return 0;
} 

最佳位置

可以将其看作连续一段未选座位看作一条线段,

选座位就相当于,每次找出最长的一段,均分成两个线段即可
离开就是,当前座位的左右两条线段合并

用 set 维护

我是屑。

点击查看代码
#include<cstdio>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
const int N = 3e5 + 5;

int n, m, a[N], ans[N], pre[N], nex[N], pos[N];
bool vis[N];

struct node {
	int l, r, pos, len;
	bool operator < (const node &b) const {
		return len == b.len ? pos < b.pos : len > b.len;
	}
	void init() {
		int tmp = r - l + 1;
		pos = l + tmp / 2;
		if(!(tmp & 1)) pos --;
		len = pos - l + 1; 
	}
};

set<node> st;
map<int, int> mp;

int main() {
	
	scanf("%d %d", &n, &m);
	st.insert((node){1, n, 1, n});//初始化 
	nex[0] = n + 1, pos[m + 1] = n + 1, mp[0] = 0, mp[n + 1] = m + 1;//设界限 
	int x;
	for(int i = 1; i <= 2 * m; i ++) {
		scanf("%d", &x);
		if(vis[x]) {//离开 
			node tmp, res;
			tmp.l = pos[pre[x]], tmp.r = pos[nex[x]] - 1;//左右的两条线段 
			if(tmp.l == 1) tmp.pos = 1, tmp.len = tmp.r;// 不用取中点,直接计算 
			else if(tmp.r == n) tmp.pos = n, tmp.len = n - tmp.l + 1;//同上 
			else tmp.init();// 计算 tmp 的各个变量 
			if(tmp.l <= pos[x] - 1) {
				res.l = tmp.l, res.r = pos[x] - 1;
				res.init(), st.erase(*st.find(res));
			}
			if(pos[x] + 1 <= tmp.r) {
				res.l = pos[x] + 1, res.r = tmp.r;
				res.init(), st.erase(*st.find(res));
			}
			st.insert(tmp);
			nex[pre[x]] = nex[x], pre[nex[x]] = pre[x];
			pos[x] = nex[x] = pre[x] = 0;
		}
		else {//选座位 
			vis[x] = 1;
			node tmp = *st.begin(), res;
			st.erase(st.begin());
			pos[x] = tmp.pos;
			res.l = tmp.l, res.r = pos[x] - 1;
			if(res.l <= res.r) {
				if(res.l == 1) res.len = res.r, res.pos = 1;
				else res.init();
				st.insert(res);
			}
			res.l = pos[x] + 1, res.r = tmp.r;
			if(res.l <= res.r) {
				if(res.r == n) res.len = n - res.l + 1, res.pos = n;
				else res.init();
				st.insert(res);
			}
			ans[x] = pos[x];
			pre[x] = mp[tmp.l - 1], nex[x] = mp[tmp.r + 1];
			nex[pre[x]] = x, pre[nex[x]] = x;
			mp[pos[x]] = x;
		}
		
	} 
	
	for(int i = 1; i <= m; i ++) printf("%d\n", ans[i]);
	return 0;
}

牛客提高组第五场

智乃的差分

也不知道过没过。。。

分类讨论,就是需要考虑的情况有点多。


#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int t, n, x, a[N], cnt, siz, tag;
struct node {
	int val, tot;
	node() {}
	node(int x, int y) {
		val = x, tot = y;
	}
}b[N];
bool cmp(node x, node y) {
	return x.tot > y.tot;
}
void read(int &x) {
	x = 0; 
	int f = 1;
	char s = getchar();
	while(s < '0' || s > '9') {
		if(s == '-') f = -1;
		s = getchar();
	}
	while(s <= '9' && s >= '0') x = x * 10 + s - '0', s = getchar();
	x *= f;
}
int main() {
	freopen("A.in", "r", stdin);
	freopen("A.out", "w", stdout);
	read(t);
	while(t --) {
		read(n), read(x);
		for(int i = 1; i <= n; i ++) read(a[i]);
		sort(a + 1, a + 1 + n);
		if(x < 0) {
			puts("yes");
			for(int i = 1; i <= n; i ++) printf("%d ", a[i]);
			puts("");
		}
		else if(x == 0) {
			cnt = tag = 0, a[0] = a[1];
			for(int i = 1; i <= n; i ++) {
				if(a[i] == a[i - 1]) {
					cnt ++;
					if(cnt > (n + 1) / 2) {
						tag = 1;
						break;
					}
				}
				else cnt = 1;
			}
			if(cnt > (n + 1) / 2) tag = 1;
			if(tag) {
				puts("no");
				continue;
			}
			b[siz = 1] = (node) {a[1], 1};
			for(int i = 2; i <= n; i ++) {
				if(a[i] != a[i - 1]) {
					b[++siz] = (node) {a[i], 1};
				}
				else b[siz].tot ++;
			}
			sort(b + 1, b + 1 + siz, cmp);
			if(b[1].val == 0) {
				if(b[1].tot * 2 == n + 1) {
					puts("no");
					continue;
				}
				swap(b[1], b[2]);
			}
			puts("yes"), tag = -1;
			for(int i = 1; i <= siz; i ++) {
				while(b[i].tot) {
					if(tag + 2 > n) tag = 0;
					tag += 2, b[i].tot --;
					a[tag] = b[i].val; 
				}
			}
			for(int i = 1; i <= n; i ++) printf("%d ", a[i]);
			puts("");
		}
		else {
			if(a[n] != x) {
				puts("yes");
				for(int i = n; i; i --) printf("%d ", a[i]);	
				puts("");
			}
			else {
				int j = n - 1;
				while(a[j] == a[n] && j >= 1) j --;
				if(!j || a[j] == 0) puts("no");
				else {
					swap(a[n], a[j]);
					puts("yes");
					for(int i = n; i; i --) printf("%d ", a[i]);
					puts("");
				}
			}
		}
	}
	return 0;
}

牛牛的旅行

菜死了

给定一棵树,树上任意一条长度不为 1的路径贡献为 路径上的最大点权值 减去 其路径长度。 求贡献之和。

并查集 + 树上贡献统计。

首先,边权和点权可以分开计算,

边用 \(dfs\) 跑一遍,记录每个点的子树大小,合并的时候 加上 \(siz *(n - siz)\) 即可。

点权, 先存起来从小到大排个序, 再用并查集思想维护 每个块的大小。



#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int N = 1e6 + 5, mod = 1e9 + 7;
int n, ans, fa[N], siz[N], sum[N];
int tot, head[N], to[N << 1], nex[N << 1];
bool vis[N];
struct node {
	int val, id;
}tr[N];
bool cmp(node x, node y) {
	return x.val < y.val;
}

void add(int x, int y) {
	to[++tot] = y, nex[tot] = head[x], head[x] = tot;
}

int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void dfs(int x, int f) {
	int ver; siz[x] = 1;
	for(int i = head[x]; i; i = nex[i]) {
		ver = to[i];
		if(ver == f) continue;
		dfs(ver, x);
		siz[x] += siz[ver];
		ans = (ans - siz[ver] * (n - siz[ver]) % mod + mod) % mod;
	}
}

void connect(int x, int y) {
	int f = find(x), ff = find(y);
	if(f != ff) {
		sum[f] += sum[ff];
		fa[ff] = f;
	}
}

signed main() {
	
	scanf("%lld", &n);
	for(int i = 1; i <= n; i ++) scanf("%lld", &tr[i].val), tr[i].id = fa[i] = i, sum[i] = 1;
	int u, v;
	for(int i = 1; i < n; i ++) {
		scanf("%lld %lld", &u, &v);
		add(u, v), add(v, u);
	}
	
	dfs(1, 0);
	
	sort(tr + 1, tr + 1 + n, cmp);
	int ver;
	for(int i = 1; i <= n; i ++) {
		for(int j = head[tr[i].id]; j; j = nex[j]) {
			ver = to[j];
			if(vis[ver]) {
				u = find(tr[i].id), v = find(ver);
				ans = (ans + tr[i].val * sum[u] % mod * sum[v] % mod) % mod;
				connect(tr[i].id, ver);
			}
		}
		vis[tr[i].id] = 1;
	}
	printf("%lld", ans * 2 % mod);
	return 0;
} 

牛客提高组第四场

最终测试

阿西吧,期望水题?

因为一个选手的得分情况只有 \(4\) 种, 直接算出来就好。

最后的 \(/16\) 是因为 每两个选手 各有四种情况, 概率均分。

直接计算对于每种取值,有多少种取值大于它。(排第几名)

注意要减去自身可能大于的情况。

水……


#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int N = 1e5 + 5;
int n, cnt, val[N << 2], v[2], a[N][4], ans[N];
bool cmp(int x, int y) {
	return x > y;
}
int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) {
		scanf("%d %d", &v[0], &v[1]);
		a[i][0] = 0, a[i][1] = v[0], a[i][2] = v[1], a[i][3] = v[1] + v[0];
		for(int j = 0; j < 4; j ++) val[++cnt] = a[i][j];
	}
	sort(val + 1, val + 1 + cnt, cmp);
	for(int i = 1; i <= n; i ++) {
		for(int j = 0; j < 4; j ++) {
			int id = lower_bound(val + 1, val + 1 + cnt, a[i][j], cmp) - val - 1;
			for(int k = 0; k < 4; k ++) {
				if(a[i][k] > a[i][j]) id --;
			}
			ans[i] += id;
		}
	}
	for(int i = 1; i <= n; i ++) {
		printf("%f\n", ans[i] / 16.0 + 1.0);
	}
	return 0;
}

空间跳跃

角谷猜想是什么神笔东西。虽然我不知道为什么它是对的,就很妙

正难则反嘛

倒退就是 从 n 到 1

根据定理,正整数直接可以推到 1

负整数 在根据操作 1 搞成正整数, 再搞到 1 就可以了。

我不理解!


#include<cstdio>
#include<algorithm>
using namespace std;
int q, d, l, n, cnt, a[2000]; 
int main() {
	scanf("%d %d %d", &q, &d, &l);
	while(q --) {
		scanf("%d", &n);
		a[cnt = 1] = n;
		while(!n && n != -1 && n != -5 && n != -17) {
			if(n & 1) n = n * 3 + 1;
			else n /= 2;
			a[++cnt] = n;
		} 
		while(n <= 0) n += d, a[++cnt] = n;
		while(n != 1) {
			if(n & 1) n = n * 3 + 1;
			else n /= 2;
			a[++cnt] = n;
		}
		printf("%d\n", cnt - 1);
		for(int i = cnt; i; i --) printf("%d ", a[i]);
		puts("");
	}
	return 0;
} 

树网的核

树网的核

依旧是很恶心的题面

不愧是 \(CCF\) 语言((

直接根据树的直径的性质来搞就好了。

可以肯定的是,非直径上的点到直径的距离的最大值一定是最小的

所以先跑两遍 \(dfs\) 求出直径,在跑一遍非直径,取最大值就好了。



#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 305;
int n, m, tot, ans = 1e9, head[N], nex[N << 1], to[N << 1], w[N << 1];
void add(int x, int y, int z) {
	to[++tot] = y, nex[tot] = head[x], head[x] = tot, w[tot] = z;
}
bool tag[N];
int fa[N], dis[N], mx;

void dfs(int x, int f) {
	fa[x] = f; int ver;
	if(dis[x] > dis[mx]) mx = x;
	for(int i = head[x]; i; i = nex[i]) {
		ver = to[i];
		if(tag[ver] || ver == f) continue;
		dis[ver] = dis[x] + w[i];
		dfs(ver, x); 
	}
}
int main() {
	scanf("%d %d", &n, &m);
	int u, v, ww;
	for(int i = 1; i < n; i ++) {
		scanf("%d %d %d", &u, &v, &ww);
		add(u, v, ww), add(v, u, ww);
	}
	dis[1] = 1, dfs(1, 0), dis[mx] = 0, dfs(mx, 0);
	u = mx;
	for(int i = u, j = u; i; i = fa[i]) {
		while(dis[j] - dis[i] > m) j = fa[j];
		ans = min(ans, max(dis[u] - dis[j], dis[i]));
	}
	for(int i = u; i; i = fa[i]) tag[i] = 1;
	for(int i = u; i; i = fa[i]) {
		mx = i, dis[i] = 0;
		dfs(i, fa[i]);
	}
	for(int i = 1; i <= n; i ++) ans = max(ans, dis[i]);
	printf("%d\n", ans); 
	
	return 0;
} 

我鸽得理不直气也壮!

posted @ 2021-10-14 20:19  Spring-Araki  阅读(18)  评论(0编辑  收藏  举报