\(\cal T_1\) 轻松的音乐 / kon

Description

数轴上有 \(n\) 个位置,在位置 \(i\) 上可以覆盖位置 \([i-p_i,i)\) 或者 \((i,i+p_i]\),如果最后位置 \(i\) 没有被覆盖则会产生 \(a_i\) 的代价,最小化代价和,并输出这个代价。

\(n\leqslant 50000,0\leqslant p_i\leqslant 30,0\leqslant a_i\leqslant 10^4\).

Solution

一些闲话:这几天好不容易有一道想出正解的题,但是没调出来 qwq.

\(dp(i,j)\) 为前 \(i\) 个数,配到 \(i+j\) 的最小代价。

  • \(dp(i,j)\gets dp(i-1,j+1)\)

  • \(dp(i,j)\gets dp(i-1,j)+a_{i+j}\)

  • 考虑 \(i\) 向右的情况

    \[dp(i,p_i)\gets \min_{-30\leqslant k\leqslant p_i+1}\left\{dp(i-1,k)+\sum_{t=i+k}^i a_t\right\} \]

  • 考虑 \(i\) 向左的情况(需要注意的是,\(k\) 可以等于 \(i-1\)

    \[dp\left(i,\max\left\{i-1,\max_{k<q<i}\{q+p_q\}\right\}-i\right)\gets dp(k,\max\{i-p_i-1,0\}-k) \]

  • 对于每个 \(i\),最后要做一个从左往右啥也不干的递推,再做一个后缀 \(\min\).

时间复杂度 \(\mathcal O(np)\)是不是很水啊?水就对了,因为这是我的草稿

Code

# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
    T x=0; char s; bool f=0;
    while(!isdigit(s=getchar())) f|=(s=='-');
    for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
    return f? -x: x;
}
template <class T>
inline void write(T x) {
    static int writ[50], w_tp=0;
    if(x<0) putchar('-'), x=-x;
    do writ[++w_tp]=x-x/10*10, x/=10; while(x);
    while(putchar(writ[w_tp--]^48), w_tp);
}

# include <cstring>
# include <iostream>
using namespace std;

const int up = 30;
const int MAXN = 5e4+5;
const int infty = 0x3f3f3f3f;

int dp[MAXN][65];
int n, p[MAXN], a[MAXN];

int val(int l,int r) {
    if(l>r) return 0;
    return a[r]-a[l-1];
}

int main() {
    freopen("kon.in","r",stdin);
    freopen("kon.out","w",stdout);
    n=read(9); memset(dp,0x3f,sizeof dp);
    for(int i=1;i<=n;++i) p[i]=read(9);
    for(int i=1;i<=n;++i) a[i]=a[i-1]+read(9);
    dp[0][up]=0;
    for(int i=1;i<=n;++i) {
        for(int j=0; j<=up+up; ++j) {
            if(j<up+up) dp[i][j]=dp[i-1][j+1];
            if(i+j-up<=n && i+j-up>0) dp[i][j] = min(
                dp[i][j], dp[i-1][j]+a[i+j-up]-a[i+j-up-1]);
        } 
        if(p[i]) { int tmp = infty;
            for(int j=0; j<=p[i]+1+up; ++j) if(i+j-up>0)
                tmp = min(tmp, dp[i-1][j]+val(i+j-up,i));
            dp[i][p[i]+up] = tmp; int fro = max(i-p[i]-1,0), to=-1; 
            for(int k=min(i-1,fro+up); fro-k<=up && k>=0; --k) {
                if(k!=i-1) to = max(to, min(k+1+p[k+1],n)-i);
                if(to>=-up && to<=up) 
                    dp[i][to+up] = min(dp[i][to+up], dp[k][fro-k+up]);
            } 
        }
        for(int j=max(i-up+1,1); j<=min(i+up,n); ++j)
            dp[i][j-i+up] = min(dp[i][j-i+up], dp[i][j-i+up-1]+val(j,j));
        for(int j=up+up; j>0; --j) dp[i][j-1] = min(dp[i][j-1], dp[i][j]);
    } int ans = infty;
    for(int i=1;i<=n;++i) if(i+up>=n)
        ans = min(ans, dp[i][n-i+up]);
    print(ans,'\n');
    return 0;
}

\(\cal T_2\) 论文题 / ball

Description

有一袋 \(n\) 个颜色球,第 \(i\) 个颜色的球有 \(a_i\) 个。

当袋子里至少有两个不同颜色的球时,执行以下步骤:

  1. 一个接一个地按照顺序随机取出两个球,这些球的颜色可能是一样的;
  2. 把第二个球涂成第一个球的颜色,然后把两个球放回袋子里。

所有这些动作只需要一秒钟,输出无法操作时候的期望时间对 \(10^9+7\) 取模的结果。

\(n\leqslant 2500,a_i\leqslant 10^5\).

Solution

靠着 这篇博客 勉强意会了鞅与停时定理 😵‍💫。大概抄一下:

对于随机时间序列 \(\{A_0,A_1,\dots\}\)\(t\) 为其停时,终止状态为 \(A_t\),求 \(E(t)\).

构造势能函数 \(Φ(A)\),满足:

  • \(E(\Phi(A_{n+1})-\Phi(A_n)\mid A_0,A_1,\dots,A_n)=-1\)

  • \(\Phi(A_t)\) 为常数,且 \(\Phi(A_i)=\Phi(A_t)\) 当且仅当 \(i=t\).

构造序列 \(X_i=\Phi(A_i)+i\),则 \(E(X_{n+1}-X_n\mid X_0,X_1,\dots,X_n)=0\),即 \(\{X_0,X_1,\dots\}\) 是鞅。

根据停时定理,我们可以得到 \(E(X_t)=E(X_0)\),即 \(E(t)=E(\Phi(A_0))-E(\Phi(A_t))\).

由于做题时 \(\Phi(A_0),\Phi(A_t)\) 都是常数,所以期望又等于本身。

好了,接下来就是抄题解时间 😵‍💫!

\(m=\sum a_i\),设 \(f(x)\) 表示有 \(x\) 个球的颜色的势能函数,\(\Phi(A)=\sum f(a_i)\) 表示当前状态的势能。

每一次操作都有 \(m(m-1)\) 种情况,考虑每种情况究竟是哪两个球是不易的,所以对每一种颜色 \(i\) 考虑:有 \(a_i(a_i-1)+(m-a_i)(m-a_i-1)\) 种情况 \(a_i\) 不变,有 \(a_i(m-a_i)\) 种情况变成 \(a_i-1\),另有 \(a_i(m-a_i)\) 种情况变成 \(a_i+1\),那么有

\[\frac{1}{m(m-1)}\cdot \sum (a_i(a_i-1)+(m-a_i)(m-a_i-1))\cdot f(a_i)+a_i(m-a_i)(f(a_i-1)+f(a_i+1))=\sum f(a_i)-1 \]

也就是

\[\sum \frac{a_i(m-a_i)}{m(m-1)}\cdot (f(a_i-1)+f(a_i+1)-2f(a_i))=-1 \]

由于 \(\displaystyle \sum\frac{-a_i}{m}=-1\),那么构造

\[f(a_i-1)+f(a_i+1)-2f(a_i)=\frac{-(m-1)}{m-a_i} \]

运用那少得可怜的高中数列知识,令 \(g(i)=f(i+1)-f(i)\) 就有

\[g(a_i)-g(a_i-1)=\frac{-(m-1)}{m-a_i} \]

于是可以递推出 \(g(i)\)。同时由于 \(g\) 是单减的,所以 \(A_t\) 的势能唯一。不过直接递推会 TLE,这里需要再化简一下

\[\Phi(A_t)=\sum_{i=0}^{m-1}\sum_{j=0}^{i}\frac{-(m-1)}{m-j}=\sum_{j=0}^{m-1}(m-j)\cdot\frac{-(m-1)}{m-j}=-m(m-1) \]

Code

算了不写了。

\(\cal T_3\) 最小生成树 / mst

Description

小 Y 给了你一张图,共有 \(n+1\) 个点,编号为 \(0 \sim n\),一共有两类边 :

  1. \(n\) 条边,小 Y 会给你 \(n\) 个整数 \(a_1,a_2,\dots,a_n\),其中 \(a_i\) 表示 \(0\) 号节点与 \(i\) 号节点有一条边权为 \(a_i\) 的边;

  2. \(m\) 条边,连接 \(u,v(u,v \in [1,n])\),长度为 \(w\).

显而易见,这个图是一个联通图,你需要求最小生成树。

但是不幸的是,小 Y 会做出 \(q\) 次修改,每次给出两个数 \(x,y\),表示将 \(a_x\) 改为 \(y\),你需要输出最小生成树大小。

注意,所有修改之间 不独立

\(n , m , q\leqslant 3 \cdot 10 ^ 5\).

Solution

一些闲话:感谢 \(\sf{C202044zxy}\) 的耐心讲解!给保姆级讲解点一个大大的赞,把我这个菜鸡都讲懂了 (≧∀≦)ゞ!话说什么画小人真的有够可爱的

根据部分分的提示,我们先考虑 2 类边组成一条链的情况。可以想到把它们放在线段树上维护,1 类边挂在叶子节点上,2 类边用于区间 \([l,{\rm mid}],({\rm mid},r]\) 的合并。如何合并?可以发现合并只和包含 \(l,r\) 的两个(或是一个)连通块是否与 \(0\) 连通有关(其余的部分已经和外界没有联系了),所以设 \(dp(0/1,0/1)\) 表示 \(l,r\) 分别是否与 \(0\) 连通,直接合并即可。初始时给叶子节点的 \(dp(1,1)\) 赋值 \(a_l\),其余 \(\tt dp\) 值均为零。

现在拓展到原问题,我们可以找出 2 类边的等效链(在 \(\rm kruskal\) 算法意义上),然后在链上 \(\tt dp\)。具体构造就是对 2 类边跑一个 \(\rm kruskal\),当添加边 \((u,v)\) 时,把 \(u,v\) 所在连通块对应的等效链的链头(不妨设合并的两个链头分别为 \(x,y\))合并,得到新的等效链。

考虑为啥这个构造是正确的。这实际上等价于在 \(\rm kruskal\) 过程中,加入边 \((u,v)\) 时,\(x,u\) 连通,\(y,v\) 连通。这也是比较显的,因为加入 1 类边只会让新图连通性不弱于原图连通性。

复杂度 \(\mathcal O(m\log m+16n\log n)\).

Code

为啥我写代码这么慢啊!

# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
    T x=0; char s; bool f=0;
    while(!isdigit(s=getchar())) f|=(s=='-');
    for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
    return f? -x: x;
}
template <class T>
inline void write(T x) {
    static int writ[50], w_tp=0;
    if(x<0) putchar('-'), x=-x;
    do writ[++w_tp]=x-x/10*10, x/=10; while(x);
    while(putchar(writ[w_tp--]^48), w_tp);
}

# include <cstring>
# include <iostream>
# include <algorithm>
using namespace std;
typedef long long _long;

const int MAXN = 3e5+5;
const _long infty = 1e17;

int rig[MAXN]; _long val[MAXN];
int n, m, a[MAXN], f[MAXN], L[MAXN], R[MAXN];
struct node { _long v[2][2]; } t[MAXN<<2];
struct edge {
    int u, v, w;
    bool operator < (const edge& t) const { return w<t.w; }
} E[MAXN];

void init() { for(int i=1;i<=n;++i) f[i]=i; }
int _find(int x) { return x==f[x]?x:f[x]=_find(f[x]); }
void kruskal() {
    sort(E+1,E+m+1); _long ret=0;
    for(int i=1;i<=m;++i) {
        int u=E[i].u, v=E[i].v;
        if((u=_find(u))^(v=_find(v))) {
            R[rig[u]]=v, L[v]=rig[u], 
            rig[u]=rig[v], f[v]=u, val[v]=E[i].w;
        }
    } 
}

void pushUp(int o,const _long& w) {
    const node& l=t[o<<1], r=t[o<<1|1];
    memset(t[o].v,0x3f,sizeof t[o].v);
    for(int b=0;b<2;++b) for(int c=0;c<2;++c) if(b|c)
        for(int a=0;a<2;++a) for(int d=0;d<2;++d) {
            if(!(b&c) && w==infty) continue;
            t[o].v[a][d] = min(t[o].v[a][d],
            l.v[a][b]+r.v[c][d]+((b&c)?0:w));
        } 
}
void update(int o,int l) {
    for(int i=0;i<2;++i) for(int j=0;j<2;++j)
        t[o].v[i][j] = ((i&j)?a[rig[l]]:0);
}
void build(int o,int l,int r) {
    if(l==r) return update(o,l), void();
    int mid = l+r>>1; build(o<<1,l,mid);
    build(o<<1|1,mid+1,r), pushUp(o,val[rig[mid+1]]);
}
void modify(int o,int l,int r,int p) {
    if(l==r) return update(o,l), void();
    int mid = l+r>>1; if(p<=mid) modify(o<<1,l,mid,p);
    else modify(o<<1|1,mid+1,r,p); pushUp(o,val[rig[mid+1]]);
}

void wander(int p) {
    static int i=0; ++i;
    for(; ; p=R[p], ++i) {
        rig[i]=p, f[p]=i; if(p==R[p]) return;
    }
}

int main() {
    freopen("mst.in","r",stdin);
    freopen("mst.out","w",stdout);
    n=read(9), m=read(9);
    for(int i=1;i<=n;++i) val[i]=infty,
        a[i]=read(9), L[i]=R[i]=rig[i]=i;
    for(int i=1;i<=m;++i) E[i].u=read(9),
        E[i].v=read(9), E[i].w=read(9);
    init(), kruskal(); int pos;
    for(int i=1;i<=n;++i) if(L[i]==i) wander(i);
    build(1,1,n); 
    for(int q=read(9); q; --q) {
        int x=read(9), y=read(9); a[x]=y;
        modify(1,1,n,f[x]), print(t[1].v[1][1],'\n');
    }
    return 0;
}
posted on 2022-07-22 17:09  Oxide  阅读(238)  评论(7编辑  收藏  举报