Samjia Contest 12.22

本文深入解析三道算法竞赛题目,涵盖复杂度优化、启发式合并与多项式求逆等高级技巧,通过代码示例展示高效算法设计过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:
https://floj.tech/contest/449

题解:

注意到每次只会把一段往前copy,那么一个点每次都会只会向前走,假设现在x∈[yi,yi+1),hi>0,那么x一次向左会跳yi-hi的距离,直到跳到yi左边。

这个可以直接除一下来得到跳出这段后的位置,倒着扫,x最终会定在一个点上。

如果两个点最终汇聚在一个点上,它们就是一样的。

时间复杂度 O ( ( a + q ) b ) O((a+q)b) O((a+q)b)


题解:

首先对于z考虑[l…r]包含他的条件是什么?

显然就是两个不同的子树里都有[l…r]的点。

考虑一次枚举z的子树里的点,假设现在枚举到了y子树里的x点,把x点放到y子树之前的所有子树的点中,显然取相邻的两个点是最优的(左一个,右一个),这样会有若干对(a,b)

用启发式合并就可以做到(a,b)的总对数是O(n log n),用set维护相邻,这一部分复杂度是O(n log^2 n)

若有一对(a,b)满足l<=a&&r>=n,那么[l…r]在z上就是可行的。

但是我们不可能枚举每个询问,可能是要把(a,b)整体做。

把所有(a,b)按a排序,显然b也要升序,不然就去掉,相当于做个单调栈。

然后做出若干个(ai,a_{i+1}],b_{i+1},这样只用找到l对应的那一段,然后判断r>=b_{i+1},而不是之前的存在性问题。

二维偏序的话扫描线+树状数组。

复杂度大概为 O ( n l o g 2 n + 2 n l o g n l o g ( n l o g n ) ) O(n log^2 n+2 n log n log (n log n)) Onlog2n+2nlognlog(nlogn)


题解:

我们很容易想到一种计算方法:
枚举二分图左边的点数x(0<=x<=n),这个贡献就是 C n x ∗ 3 x ( n − x ) C_{n}^x*3^{x(n-x)} Cnx3x(nx)

这显然会有重复,一种具体方案重复的次数是 2 联 通 块 个 数 2^{联通块个数} 2

于是想到一个dp:
f ( i , j ) f(i,j) f(i,j)表示i个点,j个连通块的贡献和。
g ( n ) g(n) g(n)就表示之前那种计算方法的贡献。

结合做过的城市规划,不难列出转移:

f ( i , j ) ( j &gt; 1 ) f(i,j)(j&gt;1) f(i,j)(j>1)
= ∑ x = 1 i − 1 C i − 1 x − 1 ∗ f x , 1 ∗ f i − x , j − 1 =\sum_{x=1}^{i-1}C_{i-1}^{x-1}*f_{x,1}*f{i-x,j-1} =x=1i1Ci1x1fx,1fix,j1

f ( i , 1 ) = ( g ( i ) − ∑ x = 2 i f i , j ∗ 2 j ) / 2 f(i,1)=(g(i)-\sum_{x=2}^{i}f_{i,j}*2^{j})/2 f(i,1)=(g(i)x=2ifi,j2j)/2

求出 f i , 1 f_{i,1} fi,1之后就可以算答案了,设 s ( n ) s(n) s(n)表示n个点时的答案。

s ( n ) = ∑ x = 1 n C n − 1 x − 1 ∗ f ( x , 1 ) ∗ s ( n − x ) s(n)=\sum_{x=1}^n C_{n-1}^{x-1}*f(x,1)*s(n-x) s(n)=x=1nCn1x1f(x,1)s(nx)

至此得到一个 O ( n 3 ) O(n^3) O(n3)的做法。

发现 f ( i , j ) ( j &gt; 1 ) f(i,j)(j&gt;1) f(i,j)(j>1)的转移是基本一样的,所以可以去掉后一维,设 f ( n ) f(n) f(n)直接表示n个点一个联通块的贡献。

f ( n ) = ( g ( n ) − ∑ x = 1 n − 1 C n − 1 x − 1 ∗ f ( x ) ∗ g ( n − x ) ∗ 2 ) / 2 f(n)=(g(n)-\sum_{x=1}^{n-1}C_{n-1}^{x-1}*f(x)*g(n-x)*2)/2 f(n)=(g(n)x=1n1Cn1x1f(x)g(nx)2)/2

那么复杂度就变成了 O ( n 2 ) O(n^2) O(n2)

显然f和s的转移都可以用分治NTT或多项式求逆优化,复杂度O(n log n)。

最后的瓶颈在于g。

比赛时就卡在了g,很痛苦

这是一个套路,可惜我不知道。

( ( a + b ) 2 − a 2 − b 2 ) / 2 = a b ((a+b)^2-a^2-b^2)/2=ab ((a+b)2a2b2)/2=ab
g ( n ) = ∑ x = 0 n C n x ∗ 3 x ( n − x ) g(n)=\sum_{x=0}^n C_{n}^x*3^{x(n-x)} g(n)=x=0nCnx3x(nx)
= ∑ x = 0 n C n x ∗ 3 ( n 2 − x 2 − ( n − x ) 2 ) / 2 =\sum_{x=0}^n C_{n}^x*3^{(n^2-x^2-(n-x)^2)/2} =x=0nCnx3(n2x2(nx)2)/2

这样就有一个卷积的东西了,不过因为mod 998244353意义下没有 3 \sqrt 3 3 ,所以要分奇偶讨论。

最后的结果是做两次NTT就行了。

总复杂度O(n log n),不过多项式求逆我不是很熟,所以用了cdq分治NTT。


Code:

T1:

#include<map>
#include<cstdio>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int N = 1005;

int n, a, b, q, x;

struct nod {
    int x; char c;
} c[N];

int y[N], h[N];

int la(int x) {
    fd(i, b, 1)
        if(x >= y[i] && x < y[i + 1]) {
            if(!h[i]) continue;
            int z = (x - y[i]) / (y[i] - h[i]);
            x -= (z + 1) * (y[i] - h[i]);
        }
    return x;
}

map<int, int> bz;

int main() {
    scanf("%d %d %d %d", &n, &a, &b, &q);
    fo(i, 1, a) scanf("%d %c", &c[i].x, &c[i].c);
    fo(i, 1, b) scanf("%d %d", &y[i], &h[i]);
    y[b + 1] = n + 1;
    fo(i, 1, a) bz[la(c[i].x)] = c[i].c;
    fo(i, 1, q) {
        scanf("%d", &x);
        printf("%c", bz[la(x)] ? bz[la(x)] : '?');
    }
}

T2:

#include<set>
#include<cstdio>
#include<algorithm>
#define pr printf
#define low(a) ((a) & -(a))
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int N = 3e5 + 5;

int n, m, x, y;
int fi[N], nt[N * 2], to[N * 2], tot;
struct ak {
    int l, r, i;
} q[N];

void link(int x, int y) {
    nt[++ tot] = fi[x], to[tot] = y, fi[x] = tot;
}

int siz[N], son[N], top[N], dep[N], fa[N];

void dg(int x) {
    siz[x] = 1;
    dep[x] = dep[fa[x]] + 1;
    for(int i = fi[x]; i; i = nt[i]) if(to[i] != fa[x])
        fa[to[i]] = x, dg(to[i]),
        siz[x] += siz[to[i]],
        son[x] = siz[to[i]] > siz[son[x]] ? to[i] : son[x];
}

void dfs(int x) {
    if(son[x]) top[son[x]] = top[x], dfs(son[x]);
    for(int i = fi[x]; i; i = nt[i]) if(to[i] != fa[x] && to[i] != son[x])
        top[to[i]] = to[i], dfs(to[i]);
}

set<int> s;

int d[N];

void bb(int x) {
    d[++ d[0]] = x;
    for(int i = fi[x]; i; i = nt[i]) if(to[i] != fa[x])
        bb(to[i]);
}

struct nod {
    int a, b;
    nod(int _a = 0, int _b = 0) {a = _a, b = _b;}
} a[N * 60]; int a0;

void gg() {
    if(s.size()) {
        int mi = *s.begin(), mx = *s.rbegin();
        fo(i, 1, d[0]) {
            int x = d[i];
            if(mi < x) a[++ a0] = nod(*--s.lower_bound(x), x);
            if(mx > x) a[++ a0] = nod(x, *s.upper_bound(x));
        }
    }
    fo(i, 1, d[0]) s.insert(d[i]);
}

int cmp(nod a, nod b) {
    if(a.a < b.a) return 1;
    if(a.a > b.a) return 0;
    return a.b > b.b;
}

struct edge {
    int fi[N], to[N * 60], nt[N * 60], tot;
    bool v[N * 60];
    void link(int x, int y, bool z) {
        nt[++ tot] = fi[x], to[tot] = y, v[tot] = z, fi[x] = tot;
    }
} e;

void ad(int x, int y, int r) {
    e.link(x, r, 1); e.link(y + 1, r, 0);
}

void dd(int x) {
    for(int i = fi[x]; i; i = nt[i]) if(to[i] != fa[x] && to[i] != son[x])
        dd(to[i]);
    if(son[x]) dd(son[x]);
    a0 = 0;
    d[d[0] = 1] = x; gg();
    for(int i = fi[x]; i; i = nt[i]) if(to[i] != fa[x] && to[i] != son[x]) {
        d[0] = 0; bb(to[i]);
        gg();
    }
    a[++ a0] = nod(x, x);
    sort(a + 1, a + a0 + 1, cmp);
    int z0 = 0;
    fo(i, 1, a0) {
        while(z0 > 0 && a[z0].b >= a[i].b) z0 --;
        a[++ z0] = a[i];
    } a0 = z0;
    fo(i, 1, a0) ad(a[i - 1].a + 1, a[i].a, a[i].b);
    if(x != son[fa[x]]) s.clear();
}

int f[N];
void add(int x, int y) { while(x <= n) f[x] += y, x += low(x);}
int sum(int x) {
    int s = 0;
    while(x) s += f[x], x -= low(x);
    return s;
}

int cmp2(ak a, ak b) {
    return a.l < b.l;
}

int ans[N];

int main() {
    scanf("%d %d", &n, &m);
    fo(i, 1, n - 1) {
        scanf("%d %d", &x, &y);
        link(x, y); link(y, x);
    }
    dg(1); top[1] = 1; dfs(1);
    dd(1);
    fo(i, 1, m) scanf("%d %d", &q[i].l, &q[i].r), q[i].i = i;
    sort(q + 1, q + m + 1, cmp2);
    int L = 1;
    fo(i, 1, m) {
        while(L <= q[i].l) {
            for(int j = e.fi[L]; j; j = e.nt[j])
                add(e.to[j], e.v[j] ? 1 : -1);
            L ++;
        }
        ans[q[i].i] = sum(q[i].r);
    }
    fo(i, 1, m) printf("%d\n", ans[i]);
}

T3:

#include<cstdio>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define ff(i, x, y) for(int i = x; i < y; i ++)
using namespace std;
 
const ll mo = 998244353;
 
const int N = 8e5 + 5;
int n, T;
 
ll a[N], b[N], w[N], tp;
 
ll ksm(ll x, ll y) {
    ll s = 1;
    for(; y; y /= 2, x = x  * x % mo)
        if(y & 1) s = s * x % mo;
    return s;
}
 
const ll ni3 = ksm(3, mo - 2);

const ll ni2 = ksm(2, mo - 2);

void Dft(ll *a, int n) {
    ff(i, 0, n) {
        int p = 0, q = i;
        fo(j, 1, tp) p = p * 2 + q % 2, q /= 2;
        if(i > p) swap(a[i], a[p]);
    }
    for(int m = 2; m <= n; m *= 2) {
        int h = m / 2;
        ff(i, 0, h) {
            ll W = w[i * (n / m)];
            for(int j = i; j < n; j += m) {
                int k = j + h;
                ll u = a[j], v = a[k] * W;
                a[j] = (u + v) % mo;
                a[k] = (u - v) % mo;
            }
        }
    }
}
 
void fft(ll *a, ll *b, int n) {
    ll v = ksm(3, (mo - 1) / n);
    w[0] = 1; fo(i, 1, n) w[i] = w[i - 1] * v % mo;
    Dft(a, n); Dft(b, n);
    ff(i, 0, n) a[i] = a[i] * b[i] % mo;
    fo(i, 1, n / 2) swap(w[i], w[n - i]);
    Dft(a, n); v = ksm(n, mo - 2);
    ff(i, 0, n) a[i] = (a[i] + mo) * v % mo;
}
 
ll fac[N], nf[N];
 
ll f[N], g[N], s[N];
 
void cl() { ff(i, 0, 1 << tp) a[i] = b[i] = 0;}

void dg(int x, int y) {
    if(x == y) {
        f[x] = (g[x] - 2 * fac[x - 1] * f[x] % mo + mo) * ni2 % mo;
        return;
    }
    int m = (x + y) / 2;
    dg(x, m);
    tp = 0;
    while(1 << ++ tp <= y + m - 2 * x + 1);
    cl();
    fo(i, 1, m - x + 1) a[i] = f[x + i - 1] * nf[x + i - 2] % mo;
    fo(i, 1, y - x) b[i] = g[i] * nf[i] % mo;
    fft(a, b, 1 << tp);
    fo(i, m + 1, y) f[i] = (f[i] + a[i - x + 1]) % mo;
    dg(m + 1, y);
}

void dg2(int x, int y) {
    if(x == y) {
        if(!x) s[x] ++; else
        s[x] = s[x] * fac[x - 1] % mo;
        return;
    }
    int m = (x + y) / 2;
    dg2(x, m);
    tp = 0;
    while(1 << ++ tp <= y + m - 2 * x + 1);
    cl();
    fo(i, 1, m - x + 1) a[i] = s[x + i - 1] * nf[x + i - 1] % mo;
    fo(i, 1, y - x) b[i] = f[i] * nf[i - 1] % mo;
    fft(a, b, 1 << tp);
    fo(i, m + 1, y) s[i] = (s[i] + a[i - x + 1]) % mo;
    dg2(m + 1, y);
}

int q, x;

int main() {
    n = 100000;
    fac[0] = 1; fo(i, 1, n) fac[i] = fac[i - 1] * i % mo;
    nf[n] = ksm(fac[n], mo - 2);
    fd(i, n, 1) nf[i - 1] = nf[i] * i % mo;

    while(1 << tp ++ <= n);

    fo(i, 0, n) if(i & 1) a[i] = b[i] = nf[i] * ksm(ni3, (ll) i * i / 2);
    fft(a, b, 1 << tp);
    fo(i, 1, n) g[i] = a[i];
    cl();
    fo(i, 0, n) a[i] = b[i] = nf[i] * ksm(ni3, (ll) i * i / 2);
    fft(a, b, 1 << tp);
    fo(i, 1, n) g[i] = ((a[i] - g[i] + mo) + g[i] * ni3) % mo * fac[i] % mo * ksm(3, (ll) i * i / 2) % mo;
    dg(1, n);
    dg2(0, n);
    for(scanf("%d", &q); q; q --) scanf("%d", &x), printf("%lld\n", s[x]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值