CSP-S模拟3(A层)

A. 数据恢复

考场推出一个柿子 \(b_i \times a_j > a_i \times b_j\)

那么会优先选 \(i\)

然后用优先队列维护,同时拓扑,怎么都过不了样例 \(4\)...

实际上,当前决策优秀不一定全局最优

正解考虑先把所有点入堆, 每次取堆顶, 如果他的父亲还没被选,那么在选了他的父亲之后一定会立即选他, 这个时候把他和父亲合并,并且统计他们之间的贡献,不停的重复下去,问题解决

维护他的父亲(可能之前跟其他点合并过)用并查集

然后还需要对合并的父亲进行懒惰删除

我的做法是给每个结点打时间戳

code #include

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int maxn = 300005;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 1) + (x << 3) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
struct node{
ll a, b;
int id, tim;
friend bool operator < (const node & x, const node & y){
return x.b * y.a == y.b * x.a ? x.a > y.a : x.b * y.a < y.b * x.a;
}
}a[maxn];
priority_queueQ;
int n, f[maxn], rf[maxn];
int del[maxn];
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
int main(){
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
n = read();
for(int i = 2; i <= n; ++i)rf[i] = read();
for(int i = 1; i <= n; ++i)a[i].a = read(), a[i].b = read();
for(int i = 1; i <= n; ++i)Q.push({a[i].a, a[i].b, i, 0});
for(int i = 1; i <= n; ++i)f[i] = i;
ll ans = 0;
int tim = 0;
while(!Q.empty()){
int x = Q.top().id, tm = Q.top().tim; Q.pop();
x = fa(x);
if(del[x] != tm)continue;
int nf = fa(rf[x]);
if(nf){
ans += a[nf].b * a[x].a;
a[nf].a += a[x].a;
a[nf].b += a[x].b;
f[x] = nf;
del[x] = del[nf] = 1;
Q.push({a[nf].a, a[nf].b, nf, del[nf] = ++tim});
}
}
printf("%lld\n",ans);
return 0;
}


B. 下落的小球

发现,对于一个结点, 我们可以知道前 \(x\) 次操作需要在哪棵子树(经过当前根节点的球), 对于后面的操作,与当前根节点就没有关系了,各棵子树的操作之间互不影响,分这两种情况分别乘上组合数 /可重复的排列数即可

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1000005;
const int mod = 1e9 + 7;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 1) + (x << 3) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
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 f[maxn], size[maxn], sum[maxn], n, flow[maxn], sf[maxn];
int fac[maxn], inv[maxn];
int c(int n, int m){return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;}
int main(){
	freopen("ball.in", "r", stdin);
	freopen("ball.out", "w", stdout);
	n = read();
	for(int i = 2; i <= n; ++i)f[i] = read();
	for(int i = 1; i <= n; ++i)size[i] = 1;
	for(int i = n; i; --i)size[f[i]] += size[i];
	for(int i = 1; i <= n; ++i)sum[i] = read();
	for(int i = n; i; --i)sum[f[i]] += sum[i];
	fac[0] = inv[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	inv[n] = qpow(fac[n], mod - 2); for(int i = n - 1; i > 0; --i)inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
	for(int i = 1; i <= n; ++i)flow[i] = sum[i] - size[i];
	for(int i = 1; i <= n; ++i)sf[f[i]] += flow[i];
	int ans = 1;
	for(int i = 1; i <= n; ++i)ans = 1ll * ans * fac[sf[i]] % mod * inv[flow[i]] % mod;
	--size[1];
	for(int i = 2; i <= n; ++i){
		ans = 1ll * ans * c(size[f[i]], size[i]) % mod;
		size[f[i]] -= size[i]; --size[i];
	}
	printf("%d\n",ans);
	return 0;
}

C. 消失的运算符

褐的 \(\times 1\)

两个数组记录处理了 \(i\) 个符号,用了 \(j\) 个加号,的总和,和正在处理(还能再乘)的部分之和

遇到括号可以递归下去,然后树形背包求解

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int maxn = 800005;
const int maxm = 5005;
const int mod = 1e9 + 7;
int n, k, len, m;
char c[maxn], str[maxn];
int d[maxn], sta[maxn], top;
int C[maxm][maxm]; //组合数
int cnt;
int f[maxm][maxm];//累计总和
int g[maxm][maxm];//正在处理的部分
int tmp[maxm];//临时数组
int size[maxm];//包含符号个数

int solve(int l, int r){
	if(d[l] == r)return solve(l + 1, r - 1);
	int now = ++cnt;
	if(l == r){
		f[now][0] = c[l] ^ 48;
		return now;
	}
	bool fir = true;
	for(int p = l; p < r;){
		if(c[p] != '('){++p; continue;}
		int v = solve(p, d[p]); p = d[p] + 1;
		if(fir){
			for(int i = 0; i <= size[v]; ++i)f[now][i] = g[now][i] = f[v][i];
			size[now] = size[v];
			fir = false;
			continue;
		}
		for(int i = 0; i <= size[now]; ++i){
			for(int j = 0; j <= size[v]; ++j){
				tmp[i + j + 1] = (tmp[i + j + 1] + 1ll * f[now][i] * C[size[v]][j] % mod + 1ll * f[v][j] * C[size[now]][i] % mod) % mod;//如果是加号, 那么乘上组合数代表每个答案被统计了几次,用其来更新答案
				tmp[i + j] = (tmp[i + j] + 1ll * (f[now][i] - g[now][i] + mod) % mod * C[size[v]][j] % mod + 1ll * g[now][i] * f[v][j] % mod) % mod;//乘号
			}
		}
		for(int i = 0; i <= size[now] + size[v] + 1; ++i)f[now][i] = tmp[i], tmp[i] = 0;
		for(int i = 0; i <= size[now]; ++i)
			for(int j = 0; j <= size[v]; ++j){
				tmp[i + j] = (tmp[i + j] + 1ll * g[now][i] * f[v][j] % mod) % mod;//乘号
				tmp[i + j + 1] = (tmp[i + j + 1] + 1ll * C[size[now]][i] * f[v][j] % mod) % mod;//加号
		}
		for(int i = 0; i <= size[now] + size[v] + 1; ++i)g[now][i] = tmp[i], tmp[i] = 0;
		size[now] += size[v] + 1;
	}
	return now;
}
int main(){
	freopen("operator.in", "r", stdin);
	freopen("operator.out", "w", stdout);
	scanf("%d%d",&n, &k);
	scanf("%s", str + 1);
	for(int i = 1; i <= n; ++i){
		if(str[i] >= '0' && str[i] <= '9'){
			c[++len] = '('; c[++len] = str[i]; c[++len] = ')';
		}else{
			c[++len] = str[i];
			m += str[i] == '-';
		}
	}
	n = len;
	for(int i = 1; i <= n; ++i){
		if(c[i] == '(')sta[++top] = i;
		else if(c[i] == ')'){
			d[i] = sta[top];
			d[sta[top]] = i;
			--top;
		}
	}
	for(int i = 0; i <= m; ++i){
		C[i][0] = 1;
		for(int j = 1; j <= i; ++j)C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
	}
	printf("%d\n", f[solve(1, n)][k]);
	return 0;
}

D. 古老的序列问题

褐的 \(\times 2\)

猫树?

对询问离线分治处理,每次处理跨中点的询问

如果询问的是整区间,则不进行递归,否则空间会炸

处理跨中点的询问时,枚举一侧的端点,按照最大最小值都在该侧/其中一个在另一侧/两个都在另一侧分情况处理,开四棵线段树,分别维护应该对应乘上的系数,每次区间修改

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int maxn = 100005;
const int mod = 1e9 + 7;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 1) + (x << 3) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
int n, m, s[maxn], ans[maxn];
struct tree{
	struct node{int tag, sum, size;}t[maxn << 2 | 1];
	void built(int x, int l, int r, int *a){
		t[x].sum = t[x].tag = 0;
		if(l == r){
			t[x].size = a[l];
			return;
		}
		int mid = (l + r) >> 1;
		built(x << 1, l, mid, a);
		built(x << 1 | 1, mid + 1, r, a);
		t[x].size = (t[x << 1].size + t[x << 1 | 1].size) % mod;
	}
	void push_down(int x){
		int ls = x << 1, rs = x << 1 | 1;
		t[ls].sum = (t[ls].sum + 1ll * t[ls].size * t[x].tag) % mod;
		t[ls].tag = (t[ls].tag + t[x].tag) % mod;
		t[rs].sum = (t[rs].sum + 1ll * t[rs].size * t[x].tag) % mod;
		t[rs].tag = (t[rs].tag + t[x].tag) % mod;
		t[x].tag = 0;
	}
	void modify(int x, int l, int r, int L, int R, int val){
		if(R < l || L > r)return;
		if(L <= l && r <= R){
			t[x].sum = (t[x].sum + 1ll * t[x].size * val % mod) % mod;
			t[x].tag = (t[x].tag + val) % mod;
			return;
		}
		int mid = (l + r) >> 1;
		if(t[x].tag)push_down(x);
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
		t[x].sum = (t[x << 1].sum + t[x << 1 | 1].sum) % mod;
	}
	int query(int x, int l, int r, int L, int R){
		if(R < l || L > r)return 0;
		if(L <= l && r <= R)return t[x].sum;
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1, ans = 0;
		if(L <= mid)ans = query(x << 1, l, mid, L, R);
		if(R > mid)ans = (ans + query(x << 1 | 1, mid + 1, r, L, R)) % mod;
		return ans;
	}
}t[4];
struct node{
	int l, r, id;
	friend bool operator < (const node &x, const node &y){
		return x.l > y.l;
	}
};
vector<node>q[maxn << 2 | 1];
int f[maxn << 2 | 1], mi[maxn], mx[maxn], mn[maxn], mu[maxn];
void solve(int x, int l, int r){
	if(l == r){
		f[x] = 1ll * s[l] * s[l] % mod;
		for(node v : q[x])ans[v.id] = (ans[v.id] + f[x]) % mod;
		return;
	}
	int mid = (l + r) >> 1;
	vector<node>now;
	for(node v : q[x])if(v.l <= mid && v.r > mid && !(v.l == l && v.r == r))now.push_back(v);
	sort(now.begin(), now.end());
	mi[mid] = mod; mx[mid] = 0;
	for(int i = mid + 1; i <= r; ++i){
		mn[i] = 1;
		mi[i] = min(s[i], mi[i - 1]);
		mx[i] = max(s[i], mx[i - 1]);
		mu[i] = 1ll * mi[i] * mx[i] % mod;
	}
	t[0].built(1, mid + 1, r, mn);
	t[1].built(1, mid + 1, r, mi);
	t[2].built(1, mid + 1, r, mx);
	t[3].built(1, mid + 1, r, mu);
	int p1 = mid, p2 = mid, nmi = mod, nmx = 0, p = 0;
	for(int i = mid; i >= l; --i){
		nmi = min(nmi, s[i]);
		nmx = max(nmx, s[i]);
		while(p1 < r && mi[p1 + 1] > nmi) ++p1;
		while(p2 < r && mx[p2 + 1] < nmx) ++p2;
		t[0].modify(1, mid + 1, r, mid + 1, min(p1, p2), 1ll * nmi * nmx % mod);
		if(p1 < p2)t[1].modify(1, mid + 1, r, p1 + 1, p2, nmx);
		if(p2 < p1)t[2].modify(1, mid + 1, r, p2 + 1, p1, nmi);
		t[3].modify(1, mid + 1, r, max(p1, p2) + 1, r, 1);
		while(p < now.size() && now[p].l >= i){
			ans[now[p].id] = (ans[now[p].id] + 0ll + t[0].query(1, mid + 1, r, mid + 1, now[p].r) + t[1].query(1, mid + 1, r, mid + 1, now[p].r) + t[2].query(1, mid + 1, r, mid + 1, now[p].r) + t[3].query(1, mid + 1, r, mid + 1, now[p].r)) % mod;
			++p;
		}
	}
	f[x] = (0ll + t[0].query(1, mid + 1, r, mid + 1, r) + t[1].query(1, mid + 1, r, mid + 1, r) + t[2].query(1, mid + 1, r, mid + 1, r) + t[3].query(1, mid + 1, r, mid + 1, r)) % mod;
	for(node v : q[x]){
		if(v.l == l && v.r == r)continue;
		if(v.r <= mid)q[x << 1].push_back(v);
		else if(v.l > mid)q[x << 1 | 1].push_back(v);
		else{
			node d1 = v, d2 = v;
			d1.r = mid;
			d2.l = mid + 1;
			q[x << 1].push_back(d1);
			q[x << 1 | 1].push_back(d2);
		}
	}
	solve(x << 1, l, mid);
	solve(x << 1 | 1, mid + 1, r);
	f[x] = (0ll + f[x] + f[x << 1] + f[x << 1 | 1]) % mod;
	for(node v : q[x])if(v.l == l && v.r == r)ans[v.id] = (ans[v.id] + f[x]) % mod; 
}
int main(){
	freopen("sequence.in", "r", stdin);
	freopen("sequence.out", "w", stdout);
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)s[i]	= read();
	for(int i = 1; i <= m; ++i){
		int l = read(), r = read();
		q[1].push_back({l, r, i});
	}
	solve(1, 1, n);
	for(int i = 1; i <= m; ++i)printf("%d\n",ans[i]);
	return 0;
}
posted @ 2022-09-11 21:14  Chen_jr  阅读(82)  评论(0编辑  收藏  举报