Loading

ABC240

D

Content

依次向一个序列里插入写有数字的球,当出现连续球上数字个的相同的球时就将它们消去,维护每一次插入后的序列中球的个数。

Sol

把序列换成栈模拟就好了

Code
#include<bits/stdc++.h>
using namespace std;
const int _=2e5+5;
int n,a[_],stk[_],tp,cnt[_];
int main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;++i){
        cin>>a[i];
        if(a[i]==a[stk[tp]]) cnt[i]=cnt[stk[tp]];
        ++cnt[i];
        if(cnt[i]==a[i]){
            tp-=a[i]-1;
        }
        else stk[++tp]=i;
        cout<<tp<<endl;
    }
    return 0;
}

E

Content

给每个结点赋一个区间,父亲的区间必须包含所有儿子的区间,一个结点的子树不包含另外一个结点的两个点的区间交为空集,求根结点 \(1\) 的区间右端点最小值。

Sol

显然每个叶子赋单点区间,从 \(1\) 开始一直赋,然后父亲的区间由儿子的区间合并就好了,每个节点维护区间的左右端点就很好做,左端点一直取 \(\min\),右端点一直取 \(\max\)

Code
#include <bits/stdc++.h>
using namespace std;
const int _=2e5+5;
int n,cnt,L[_],R[_];
basic_string<int> E[_];
void dfs(int u,int fa){
    L[u]=n+1,R[u]=0;
    if(u>1&&E[u].size()==1) L[u]=R[u]=++cnt;
    for(int v:E[u]) if(v^fa) dfs(v,u),L[u]=min(L[u],L[v]),R[u]=max(R[u],R[v]);
}
int main(){
    ios::sync_with_stdio(0); cin.tie();
    cin>>n;
    for(int i=1;i<n;++i){
        int x,y; cin>>x>>y;
        E[x]+=y,E[y]+=x;
    }
    dfs(1,0);
    for(int i=1;i<=n;++i){
        cout<<L[i]<<' '<<R[i]<<'\n';
    }
    return 0;
}

F

Content

求序列前缀和的前缀和的最大值,这个序列会很长,但是相同元素的极长段个数是 \(2e5\) 的。

Sol

\(A\) 的和式写出来,发现是二次函数: \(A(cnt_{i-1}+n) - A(cnt_{i-1}) = \dfrac{n(n+1)}{2}x_i+n\cdot B(i-1)\)\(A,B\) 如题,\(cnt_i\) 为第 \(i\) 段结束后的当前序列长度),把相同的元素合成一段,贪心一下知道只用每段考虑,所以枚举相邻的两段元素,直接二次函数求最值(左右端点,对称轴)就好了

Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int _ = 2e5 + 5;
int n, m, B[_], C[2][_];
int f(int n, int i) {
    return n * (n + 1) / 2 * C[0][i] + B[i-1] * n;
}
void solve() {
    cin >> n >> m;
    int sum = 0, ans = -1e18;
    for (int i = 1; i <= n; ++i) {
        cin >> C[0][i] >> C[1][i];
        B[i] = B[i-1] + C[0][i] * C[1][i];
    }
    for (int i = 1; i <= n; ++i) {
        int y = max(f(1, i), f(C[1][i], i));
        if (C[0][i] < 0) {
            long double p = -(C[0][i]+2.0*B[i-1])/2.0/C[0][i];
            if (1 < p && p < C[1][i]) {
                y = max(y, f((int)floor(p), i));
                y = max(y, f((int)ceil(p), i));
            }
        }
        ans = max(ans, y + sum);
        sum += f(C[1][i], i);
    }
    cout << ans << '\n';
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--) solve();
    return 0;
}

G

Content

\((0,0,0)\) 走到 \((x,y,z)\) 花恰好 \(n\) 步,每一步只能选一个坐标 \(\pm 1\),空间是无限大的。

Sol

先考虑一维怎么做(从 \(0\) 走到 \(x\) 花恰好 \(n\) 步)

考虑先花 \(x\) 步走到终点,再在其中插入一些来回走抵消的,所以显然:

有解条件 \(n\ge x, 2|(n-x)\)

考虑最终我们会有 \(x+\dfrac{n-x}{2}\)\(+1\)\(\dfrac{n-x}{2}\)\(-1\) 随便放,很明显只用考虑放进 \(+1\)\(-1\) 中的一种就行了,方案等于 \(\dbinom{n}{\frac{n-x}{2}} = \dbinom{n}{\frac{n+x}{2}}\)

再考虑二维怎么做:

我们发现如果我们让 \((x,y) \to (x+y,x-y)\),那么这是一个满射,也就是原坐标系与新坐标系一一对应,而原来的操作是 \((\pm1,0),(0,\pm 1)\),新坐标系中是 \((1,1),(1,-1),(-1,1),(-1,-1)\)。这样对于单独的 \(x,y\) 都可以任意选了,因为另一个维度总有一个操作能够对应,所以 \(x,y\) 独立。

那么再套用一维的做法,另 \(f1(n,x)\) 表示一位中的问题,那么现在的方案就等于 \(f2(n,x,y)=f1(n,x+y)\cdot f1(n,x-y)\)

再再考虑怎么拓展到三维:

由于二维及以前的我们都可以预处理组合数 \(O(1)\) 求出,所以只用枚举 \(z\) 轴用了 \(i\) 步,单独用一维的方法算方案,然后再将其插入到剩下的两维的方案中即可,因为 \(z\) 的操作独立于 \(x,y\),所以直接选 \(n\) 步中哪几步是 \(z\) 就好了。

\(Ans=\sum\limits_{i=0}^n f1(i,z)\cdot \binom{n}{i}\cdot f2(n-i,x,y)\)

很好的转化,如果实在想不到也可以枚举二维问题要走几个来回,然后推式子发现可以范德蒙德卷积卷掉,更朴实一点,但是核心是三维转二维,二维 \(O(1)\) 求。

Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int _ = 1e7 + 5, mod = 998244353;
int n, x, y, z, ans;
int fac[_], inv[_];
int C(int x, int y) {
    return x < y || x < 0 || y < 0 ? 0 : fac[x] * inv[y] % mod * inv[x - y] % mod;
}
int f1(int n, int x) {
    if ((n + x) & 1) return 0;
    return C(n, (n + x) / 2);
}
int f2(int n, int x, int y) {
    return f1(n, x + y) * f1(n, x - y) % mod;
}
signed main() {
    cin >> n >> x >> y >> z;
    x = abs(x), y = abs(y), z = abs(z);
    if (n < x + y + z) return cout << 0, 0;
    fac[0] = fac[1] = inv[0] = inv[1] = 1;
    for (int i = 2; i <= n; ++i) fac[i] = fac[i - 1] * i % mod;
    for (int i = 2; i <= n; ++i) inv[i] = inv[mod % i] * (mod - mod / i) % mod;
    for (int i = 2; i <= n; ++i) inv[i] = inv[i - 1] * inv[i] % mod;
    for (int i = 0; i <= n; ++i) {
        ans = (ans + f2(n - i, x, y) * f1(i, z) % mod * C(n, i) % mod) % mod;
    }
    cout << ans;
    return 0;
}

偏简单的场,普及组不想推和式切到E也很轻松的。

posted @ 2022-09-08 22:45  Quick_Kk  阅读(44)  评论(0编辑  收藏  举报