CSP-S模拟3(A层)
A. 数据恢复
考场推出一个柿子 \(b_i \times a_j > a_i \times b_j\)
那么会优先选 \(i\)
然后用优先队列维护,同时拓扑,怎么都过不了样例 \(4\)...
实际上,当前决策优秀不一定全局最优
正解考虑先把所有点入堆, 每次取堆顶, 如果他的父亲还没被选,那么在选了他的父亲之后一定会立即选他, 这个时候把他和父亲合并,并且统计他们之间的贡献,不停的重复下去,问题解决
维护他的父亲(可能之前跟其他点合并过)用并查集
然后还需要对合并的父亲进行懒惰删除
我的做法是给每个结点打时间戳
code
#includeusing 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_queue
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;
}