CSP-S模拟12

A. 开挂

排序后从大到小考虑,并查集记录值为 \(val\) 时最小的能放的

这样小的数跳的步数尽量多,\(b\) 排序后倒着乘起来

或者用栈也可以,参考 \(Delov\) 大佬的题解吧

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 = 1000005;
int n, a[maxn], b[maxn], c[maxn];
unordered_map<int, int>mp;

int main(){
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)b[i] = read();
	sort(a + 1, a + n + 1);
	sort(b + 1, b + n + 1);
	for(int i = n; i > 0; --i){
		int val = a[i], x = mp[val];
		while(x){val = x; x = mp[x];}
		mp[a[i]] = val + 1;
		c[i] = val - a[i];
	}
	sort(c + 1, c + n + 1);
	ull ans = 0;
	for(int i = 1; i <= n; ++i)ans = ans + (ull)c[i] * (ull)b[n - i + 1];
	printf("%llu\n",ans);
	return 0;
}

B. 叁仟柒佰万

首先能证明 \(mex\) 是固定的

然后,就能得到一个转移 \(f_i\) 表示以 \(i\) 为结尾的方案数

发现需要找到一个 \(j\) 满足 \(mex(j, i) == mex\) \(mex(j + 1, i) != mex\)

那么 \(f_i\) 就是 \(f_{0} - f_{j - 1}\) 的和

发现把 \(f\) 改成前缀和,然后双指针扫一下即可

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 = 300005;
const int mod = 1e9 + 7;

int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int n, cnt[maxn];
int f[37000005], a[37000005];
void solve(){
	int mex = 0; while(cnt[mex])++mex;
	int up = min(maxn - 2, mex);
	for(int i = 0; i <= up; ++i)cnt[i] = 0;
	if(mex == 0){
		printf("%d\n",qpow(2, n - 1));
		return;
	}
	f[0] = 1;
	int ls = 0, pr = 1;
	for(; pr <= n; ++pr){
		f[pr] = 1;
		if(a[pr] < mex){
			ls += (cnt[a[pr]] == 0); ++cnt[a[pr]];
			if(ls == mex)break;
		}
	}
	++f[pr++];
	int pl = 0;
	for(; pr <= n; ++pr){
		if(a[pr] < mex){++cnt[a[pr]];}
		while(pl < pr && (a[pl + 1] > mex || cnt[a[pl + 1]] > 1)){
			++pl; if(a[pl] < mex)--cnt[a[pl]];
		}
		f[pr] = f[pl];
		f[pr] = (f[pr] + f[pr - 1]) % mod;
	}
	printf("%d\n", (f[n] - f[n - 1] + mod) % mod);
}

int main(){
	int t = read();
	for(int ask = 1; ask <= t; ++ask){
		n = read();
		if(n == 37000000){
			ll x = read(), y = read();
			for(int i = 2; i <= n; ++i)++cnt[a[i] = (a[i - 1] * x + y + i) & 262143];
		}
		else {
			for(int i = 0; i <= n; ++i)cnt[i] = 0;
			for(int i = 1; i <= n; ++i)++cnt[a[i] = read()];
		}
		solve();
	}
	return 0;
}

C. 超级加倍

建立两棵笛卡尔树,一棵满足大根堆,一棵满足小根堆(但是建出来的不是二叉树)

类比数组建立笛卡尔树按照权值顺序加的那种

然后我们要做的就是找在两棵树上都是祖先关系的点对数量

发现按照其中一棵树的 \(dfs\) 序建立树状数组

在另一棵树上 \(dfs\) ,遇到一个点查询对应子树区间得到答案,把他加进 \(BIT\) 对其子树贡献

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 = 2000005;
int n, fa[maxn];
struct graph{
	int head[maxn], tot;
	struct edge{int to, net;}e[maxn << 1 | 1];
	void add(int u, int v){
		e[++tot].net = head[u];
		head[u] = tot;
		e[tot].to = v;
	}
}T;
ll ans = 0;
struct dkr{
	graph d;
	int f[maxn];
	int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
	int dfn[maxn], tim, dfr[maxn];
	void dfs(int x){
		dfn[x] = ++tim;
		for(int i = d.head[x]; i; i = d.e[i].net){
			int v = d.e[i].to;
			dfs(v);
		}
		dfr[x] = tim;
	}
}mi, mx;
struct BIT{
	int t[maxn];
	int lowbit(int x){return x & -x;}
	void add(int x, int val){
		while(x <= n){
			t[x] += val;
			x += lowbit(x);
		}
	}
	int query(int x){
		int ans = 0;
		while(x){
			ans += t[x];
			x -= lowbit(x);
		}
		return ans;
	}
}t;
void dfs(int x){
	ans += t.query(mx.dfr[x]) - t.query(mx.dfn[x] - 1);
	t.add(mx.dfn[x], 1);
	for(int i = mi.d.head[x]; i; i = mi.d.e[i].net){
		int v = mi.d.e[i].to;
		dfs(v);
	}
	t.add(mx.dfn[x], -1);
}
int main(){
	n = read();
	for(int i = 1; i <= n; ++i)fa[i] = read();
	for(int i = 2; i <= n; ++i)T.add(fa[i], i), T.add(i, fa[i]);
	for(int x = 1; x <= n; ++x){
		mx.f[x] = x;
		for(int i = T.head[x]; i; i = T.e[i].net){
			int v = T.e[i].to;
			if(v < x){
				v = mx.fa(v);
				mx.d.add(x, v);
				mx.f[v] = x;
			}
		}
	}
	for(int x = n; x >= 1; --x){
		mi.f[x] = x;
		for(int i = T.head[x]; i; i = T.e[i].net){
			int v = T.e[i].to;
			if(v > x){
				v = mi.fa(v);
				mi.d.add(x, v);
				mi.f[v] = x;
			}
		}
	}
	mx.dfs(n);
	dfs(1);
	printf("%lld\n",ans);
	return 0;
}

D. 欢乐豆

发现更改边权的点有限,考虑把他们拎出来

把边看成无向的,得到一些联通块,每次处理一个联通块

考虑 \(Dij\) 的过程,我们用线段树进行加速

处理出某个点作为源点时到达联通块内其他点的最短路

联通块内的点可以通过联通块内边更新,也可以通过不直接相连的点的点权更新

还有就是跳到联通块外最小点再跳回,这个出去统计答案时候取 \(min\) 即可

处理完后再处理到联通块外的

源点到外面的点是直接到和通过联通块间接到取 \(min\)

孤点贡献可以直接算

code


#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 100005;
const ll inf = 0x3f3f3f3f;
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;
}
ll ans;
int n, m, a[maxn];
multiset<int>s;
vector<int>block[maxn];
vector<pii>g[maxn];
int f[maxn], size[maxn];
int fa(int x){return x == f[x] ? x : f[x] = fa(f[x]);}
void merge(int x, int y){
	x = fa(x); y = fa(y);
	if(x != y){
		if(size[y] > size[x])swap(x, y);
		size[x] += size[y];
		f[y] = x;
	}
}
struct tree{
	struct node{
		int mi, pos, tag;
		bool del;
	}t[maxn << 2 | 1];
	void built(int x, int l, int r){
		t[x] = {inf, l, inf, 0};
		if(l == r)return;
		int mid = (l + r) >> 1;
		built(x << 1, l, mid);
		built(x << 1 | 1, mid + 1, r);
	}
	void push_up(int x){
		int ls = x << 1, rs = x << 1 | 1;
		t[x].mi = min(t[ls].mi, t[rs].mi);
		if(t[x].mi == t[ls].mi && !t[ls].del)t[x].pos = t[ls].pos;
		else t[x].pos = t[rs].pos;
		t[x].del = t[ls].del & t[rs].del;
	}
	void push_down(int x){
		int ls = x << 1, rs = x << 1 | 1;
		if(!t[ls].del){
			t[ls].mi = min(t[ls].mi, t[x].tag);
			t[ls].tag = min(t[ls].tag, t[x].tag);
		}
		if(!t[rs].del){
			t[rs].mi = min(t[rs].mi, t[x].tag);
			t[rs].tag = min(t[rs].tag, t[x].tag);
		}
		t[x].tag = inf;
	}
	void delet(int x, int l, int r, int pos){
		if(l == r){t[x].mi = inf; t[x].del = 1; return;}
		if(t[x].tag != inf)push_down(x);
		int mid = (l + r) >> 1;
		if(pos <= mid)delet(x << 1, l, mid, pos);
		else delet(x << 1 | 1, mid + 1, r, pos);
		push_up(x);
	}
	void modify(int x, int l, int r, int L, int R, int val){
		if(t[x].del || L > R)return;
		if(L <= l && r <= R){
			t[x].mi = min(t[x].mi, val);
			t[x].tag = min(t[x].tag, val);
			return;
		}
		if(t[x].tag != inf)push_down(x);
		int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid)modify(x << 1 | 1, mid  + 1, r, L, R, val);
		push_up(x);
	}
}t;
int cnt, id[maxn];
int nid[maxn], dis[maxn];
void dij(int i, int s, int si){
	t.built(1, 1, si);
	t.modify(1, 1, si, nid[s], nid[s], 0);
	dis[nid[s]] = 0;
	while(t.t[1].mi != inf){
		int x = t.t[1].pos, vx = t.t[1].mi;
		dis[x] = vx; t.delet(1, 1, si, x);
		x = block[i][x - 1];
		int nl = 1;
		for(pii v : g[x]){
			t.modify(1, 1, si, nl, nid[v.first] - 1, vx + a[x]);
			t.modify(1, 1, si, nid[v.first], nid[v.first], vx + v.second);
			nl = nid[v.first] + 1;
		}
		t.modify(1, 1, si, nl, si, vx + a[x]);
	}
}
int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)f[i] = i, size[i] = 1, s.insert(a[i]);
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read(),w = read();
		g[u].push_back(pii(v, w)); merge(u, v);
	}
	for(int i = 1; i <= n; ++i){
		sort(g[i].begin(), g[i].end());
		if(fa(i) == i && size[i] > 1)id[i] = ++cnt;
	}
	for(int i = 1; i <= n; ++i){
		if(size[fa(i)] > 1)block[id[fa(i)]].push_back(i);
		else ans += 1ll * (n - 1) * a[i];
	}
	for(int i = 1; i <= cnt; ++i){
		int tmp = 0;
		for(int v : block[i]){
			nid[v] = ++tmp; s.erase(s.find(a[v]));
		}
		int mi = s.size() ? (*s.begin()) : inf, si = block[i].size();
		for(int v : block[i]){
			dij(i, v, si);
			int mii = inf;
			for(int j = 1; j <= si; ++j)mii = min(mii, dis[j] + a[block[i][j - 1]]);
			for(int j = 1; j <= si; ++j)ans += min(dis[j], mi + mii);
			ans += 1ll * mii * (n - si);
		}
		for(int v : block[i])s.insert(a[v]);
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2022-09-26 19:20  Chen_jr  阅读(52)  评论(1编辑  收藏  举报