闲话 23.1.11

今日推歌:《君と見た星空》 by EasyPop feat. 巡音ルカ
挺好听的一首歌

我对今天 T3 的评价

image

来源是哪里呢?

杂题

挑几道最近模拟赛做到的好玩题写写。有点感性化?

1.11 T2

你看这数据范围也没想法了,要么是模拟费用流要么是 DP 转生成函数。这里是 DP。

观察可以发现行列无关,因此我们可以行列分开计数。假设最终有 \(i\) 行被恰好操作奇数次的方案是 \(f_i\),对列是 \(g_i\),则答案即为

\[\sum_{im + jn - 2ij \le k} f_i\times g_j \]

可以发现对于一个确定的 \(i\),满足条件的 \(j\) 必定是连续的前缀或后缀。因此我们可以在计算完 \(f, g\) 后枚举 \(i\) 并累加前缀和得到答案。\(f, g\) 是同构的,这里讨论 \(f_i\) 的计算。

我们对每一行建立 egf,然后只需要求幂再乘起来就行了。我们考虑每一行被操作次数的奇偶性,得到不同行对应的序列 \(\langle 1, 0, 1, 0, 1,\cdots\rangle\)\(\langle 0, 1, 0, 1, 0,\cdots\rangle\),而它们的生成函数是易求的。随后只需要选择具体的行,直接写出:

\[f_i = \binom ni \left[\frac{x^q}{q!}\right] \left(\frac{e^x - e^{-x}}{2}\right)^i \left(\frac{e^x + e^{-x}}{2}\right)^{n - i} \]

我们已经完成最抽象的第一步了,下面就是代数工业的活了。

\[\begin{aligned} f_i &= \binom ni \left[\frac{x^q}{q!}\right] \left(\frac{e^x - e^{-x}}{2}\right)^i \left(\frac{e^x + e^{-x}}{2}\right)^{n - i} \\ &= \binom ni 2^{-n} \left[\frac{x^q}{q!}\right] \left(e^x - e^{-x}\right)^i\left(e^x + e^{-x}\right)^{n - i} \\ &= \binom ni 2^{-n} \left[\frac{x^q}{q!}\right] \left(e^x + e^{-x} - 2e^{-x}\right)^i\left(e^x + e^{-x}\right)^{n - i} \\ &= \binom ni 2^{-n} \left[\frac{x^q}{q!}\right] \sum_{j=0}^i(-2e^{-x})^{i - j} \binom ij \left(e^x + e^{-x}\right)^{n - i + j} \\ &= \binom ni 2^{-n} \left[\frac{x^q}{q!}\right] \sum_{j=0}^i (-2)^{i - j}\binom ij e^{x(j - i)} \sum_{k=0}^{n - i + j} \binom{n - i + j}{k} e^{xk} e^{x(i + k - n - j)} \\ &= \binom ni 2^{-n} \sum_{j=0}^i (-2)^{i - j}\binom ij \sum_{k=0}^{n - i + j} \binom{n - i + j}{k} \left[x^q / q!\right] e^{x(2k - n)} \\ &= \binom ni 2^{-n} \sum_{j=0}^i (-2)^{i - j}\binom ij \sum_{k=0}^{n - (i - j)} \binom{n - (i - j)}{k} (2k - n)^q \end{aligned}\]

随后我们就可以采用多项式计算了。对于

\[\sum_{k=0}^{n - (i - j)} \binom{n - (i - j)}{k} (2k - n)^q \]

可以先计算

\[F[i] = \sum_{k=0}^{i} \binom{i}{k} (2k - n)^q \]

的前 \(n + 1\) 项系数后翻转。剩下的部分简单,不表。

为啥我程序不开 ll A 不了啊

code
#include <bits/stdc++.h>
using namespace std; 
#define int long long
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 2e6 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
using poly = vector<int>;
ll n, m, q, k;

// int mod;
// const int mod = 10007;
// const int mod = 469762049, g = 3;
const int mod = 998244353; const int g = 3;
// const int mod = 1004535809, g = 3;
// const int mod = 1e9 + 7;
// const int mod = 1e9 + 9;
// const int mod = 1e9 + 3579, bse = 131;
/* add / sub */ 			template<typename T1,typename T2>T1 add(T1 a,T2 b){return(a+=b)>=mod?a-mod:a;}template<typename T1,typename...Args>T1 add(T1 a,Args...b){return add(a,add(b...));}template<typename T1,typename T2>T1 sub(T1 a,T2 b){return(a-=b)<0?a+mod:a;}template<typename T1,typename...Args>T1 sub(T1 a,Args...b){return sub(a,add(b...));}template<typename T1,typename T2>void addi(T1&a,T2 b){(a+=b)>=mod?(a-=mod):true;}template<typename T1,typename...Args>void addi(T1&a,Args...b){addi(a,add(b...));}template<typename T1,typename T2>void subi(T1&a,T2 b){(a-=b)<0?(a+=mod):true;}template<typename T1,typename...Args>void subi(T1&a,Args...b){subi(a,add(b...));}
/* Fastmod / mul */ 		struct FastMod{int m;ll b;void init(int _m){m=_m;if(m==0)m=1;b=((lll)1<<64)/m;}FastMod(int _m){init(_m);}int operator()(ll a){ll q=((lll)a*b)>>64;a-=q*m;if(a>=m)a-=m;return a;}}Mod(mod);int mul(int a,int b){return Mod(1ll*a*b);}template<typename T1,typename T2>int mul(T1 a,T2 b){return Mod((long long)(1ll*a*b));}template<typename T,typename...Args>int mul(T a,Args...b){return mul(a,mul(b...));}template<typename T1,typename T2>void muli(T1&a,T2 b){a=Mod(1ll*a*b);}template<typename T1,typename...Args>void muli(T1&a,Args...b){muli(a,mul(b...));} // /* trivial multiple function(idk whether its useful*/ int mul(int a, int b) { return 1ll * a * b % mod; } template <typename T1, typename T2> int mul(T1 a, T2 b) { return (long long)(1ll * a * b) % mod; } template <typename T, typename ...Args> int mul(T a, Args ...b) { return mul(a, mul(b...)); }
/* qp fac C */              template<typename T1,typename T2>T1 qp(T1 a,T2 b){T1 ret=1;for(;b>0;a=1ll*a*a%mod,b>>=1)if(b&1)ret=mul(ret,a);return ret;}vi __fac({1,1}),__ifc({1,1}),__inv({0,1});inline void ___prep(int n){static int i=2;if(i<n)for(__fac.resize(n),__ifc.resize(n),__inv.resize(n);i<n;i++)__fac[i]=mul(i,__fac[i-1]),__inv[i]=mul((mod-mod/i),__inv[mod%i]),__ifc[i]=mul(__inv[i],__ifc[i-1]);}inline int fac(int x){return ___prep(x+1),__fac[x];}inline int ifc(int x){return ___prep(x+1),__ifc[x];}inline int inv(int x){return ___prep(x+1),__inv[x];}inline int C(int n,int m){if(n<m or n<0 or m<0)return 0;return mul(fac(n),ifc(m),ifc(n-m));}
/* sum of i^k */            int S(int n,int k){vector<int>__pre(k+4),__suf(k+4),__pw(k+4),__pri(k+4);vector<bool>__vis(k+4,0);__pw[1]=1;for(int i=2,cnt=0;i<=k+2;++i){if(!__vis[i])__pri[++cnt]=i,__pw[i]=qp(i,k);for(int j=1;j<=cnt and i*__pri[j]<=k+2;++j){__vis[i*__pri[j]]=1;__pw[i*__pri[j]]=mul(__pw[i],__pw[__pri[j]]);if(i%__pri[j]==0)break;}}rep(i,2,k+2)__pw[i]=add(__pw[i],__pw[i-1]);__pre[0]=__suf[k+3]=1;rep(i,1,k+2)__pre[i]=mul(__pre[i-1],(n-i+mod));pre(i,k+2,1)__suf[i]=mul(__suf[i+1],(n-i+mod));int tmp=0,ret=0;rep(i,1,k+2){if((k-i)&1)subi(ret,mul(__pw[i],__pre[i-1],__suf[i+1],ifc(i-1),ifc(k+2-i)));else addi(ret,mul(__pw[i],__pre[i-1],__suf[i+1],ifc(i-1),ifc(k+2-i)));}return ret;}
// /* inv 2/3/6 / phi */ 		constexpr int __var_pow(int a, int b) { int ret = 1; for (; b > 0; b >>= 1, a = 1ll * a * a % mod) if (b & 1) ret = 1ll * ret * a % mod; return ret; } constexpr int __var_phi(int x) { if (x <= 2) return 1; int ret = x; for (int i = 2; 1ll * i * i <= x; ++i)  if (x % i == 0) { ret = ret / i * (i - 1); while (x % i == 0) x /= i; } if (x > 1) ret = ret / x * (x - 1); return ret; } const int __phi_mod = __var_phi(mod); const int inv2 = __var_pow(2, __phi_mod - 1), inv3 = __var_pow(3, __phi_mod - 1), inv6 = __var_pow(6, __phi_mod - 1);

int qp(int a, int b) {
    int ret = 1;
    while (b) {
        if (b & 1) ret = 1ll * a * ret % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    } return ret;
}

int btrs[N * 3];
int initrs(int lll) {
    int len = 1; while (len < lll) len <<= 1;
    for (int i = 1; i < len; ++ i) 
        btrs[i] = (btrs[i >> 1] >> 1) | (i & 1 ? len >> 1 : 0);
    return len;
}

const int L = (1 << 21);
int w[2][L];
int IUR() {
    int w0 = qp(g, (mod - 1) / L);
    w[0][0] = w[1][0] = 1;
    for (int i = 1; i < L; ++ i)
        w[1][i] = w[0][L - i] = 1ll * w[1][i - 1] * w0 % mod;
    return 1;
} int dummy = IUR();

ull f[N * 3];
void NTT(poly& F, const int& len, const int& typ) {
    F.resize(len);
    for (int i = 0; i < len; ++ i) f[i] = F[btrs[i]];
    for (int mid = 1, t; mid < len; mid <<= 1) 
        for (int i = L / (mid << 1), j = 0; j < len; j += (mid << 1))
            for (int k = 0; k < mid; ++k)
                t = 1ll * f[j + k + mid] * w[typ][i * k] % mod,
                f[j + k + mid] = f[j + k] - t + mod,
                f[j + k] += t;
    for (int i = 0; i < len; ++ i) F[i] = f[i] % mod;
    if (typ == 1) return; int inv = qp(len, mod - 2);
    for (int i = 0; i < len; ++ i) F[i] = 1ll * F[i] * inv % mod;
}

poly mul(const poly& f, const poly& g) {
    poly F(f), G(g); 
    int o = f.size() + g.size() - 1, len = initrs(o);
    NTT(F, len, 1), NTT(G, len, 1);
    for (int i = 0; i < len; ++ i) F[i] = 1ll * F[i] * G[i] % mod;
    NTT(F, len, 0); F.resize(o);
    return F;
}

poly calcf(int n) {
    poly f, g; f.resize(n + 1), g.resize(n + 1);
    rep(i,0,n) f[i] = 1ll * qp((2 * i - n + mod) % mod, q) * ifc(i) % mod;
    rep(i,0,n) g[i] = ifc(i);
    f = mul(f, g); f.resize(n + 1);
    rep(i,0,n) f[i] = 1ll * f[i] * fac(i) % mod;
    reverse(f.begin(), f.end());
    rep(i,0,n) f[i] = 1ll * f[i] * qp(mod - 2, i) % mod * ifc(i) % mod;
    f = mul(f, g); f.resize(n + 1);
    rep(i,0,n) f[i] = 1ll * f[i] * fac(i) % mod;
    int pw = qp(inv(2), n);
    rep(i,0,n) f[i] = 1ll * C(n, i) * pw % mod * f[i] % mod;
    return f;
}

signed main() {
    file(b);
    cin >> n >> m >> q >> k; q %= (mod - 1);
    if (q == 0) return puts("1"), 0;
    poly f = calcf(n), g = calcf(m);
    rep(i,1,m) g[i] = (g[i] + g[i - 1]) % mod;
    int ans = 0;
    rep(i,0,n) {
        if (n > 2 * i) {
            if (k >= 1ll * i * m) ans = (ans + 1ll * f[i] * g[min(1ll * m, 1ll * (k - 1ll * i * m) / (n - 2ll * i))]) % mod;
        } else if (n < 2 * i) {
            int l = (1ll * i * m - k + 2 * i - n - 1) / (2 * i - n);
            if (l <= m) ans = (ans + 1ll * f[i] * (g[m] - (l <= 0 ? 0 : g[l - 1]) + mod)) % mod;
        } else if (k >= 1ll * i * m) ans = (ans + 1ll * f[i] * g[m]) % mod;
    } cout << ans << '\n';
}



1.10 T1

稍加推理可以发现每个人挂的时候的 \(d\) 是一定的,因此我们统一处理 \(d\) 相同的人,在它们之前挂衣服的位置是确定的。

确定了先前的人挂衣服的位置,我们就能得到有多少段衣钩的中点可以挂衣服,奇数长度的段和偶数长度的段贡献的位置是不一样的。统计最初有多少个位置,并 dp 目前剩了 \(p\) 段奇数长度的段,\(q\) 段偶数长度的段,这自然能得到人数,同样能得到转移的概率。到最后把这些位置标一遍就能得到目前的答案。

有一个问题是偶数长度的段该如何做转移。钦定选左侧的段并记录对称轴,最后将答案沿着对称轴取平均数即可。

code
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 1e3 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;

int mod;
// const int mod = 10007;
// const int mod = 469762049, g = 3;
// const int mod = 998244353; // const int g = 3;
// const int mod = 1004535809, g = 3;
// const int mod = 1e9 + 7;
// const int mod = 1e9 + 9;
// const int mod = 1e9 + 3579, bse = 131;
/* add / sub */ 			template<typename T1,typename T2>T1 add(T1 a,T2 b){return(a+=b)>=mod?a-mod:a;}template<typename T1,typename...Args>T1 add(T1 a,Args...b){return add(a,add(b...));}template<typename T1,typename T2>T1 sub(T1 a,T2 b){return(a-=b)<0?a+mod:a;}template<typename T1,typename...Args>T1 sub(T1 a,Args...b){return sub(a,add(b...));}template<typename T1,typename T2>void addi(T1&a,T2 b){(a+=b)>=mod?(a-=mod):true;}template<typename T1,typename...Args>void addi(T1&a,Args...b){addi(a,add(b...));}template<typename T1,typename T2>void subi(T1&a,T2 b){(a-=b)<0?(a+=mod):true;}template<typename T1,typename...Args>void subi(T1&a,Args...b){subi(a,add(b...));}
/* Fastmod / mul */ 		struct FastMod{int m;ll b;void init(int _m){m=_m;if(m==0)m=1;b=((lll)1<<64)/m;}FastMod(int _m){init(_m);}int operator()(ll a){ll q=((lll)a*b)>>64;a-=q*m;if(a>=m)a-=m;return a;}}Mod(mod);int mul(int a,int b){return Mod(1ll*a*b);}template<typename T1,typename T2>int mul(T1 a,T2 b){return Mod((long long)(1ll*a*b));}template<typename T,typename...Args>int mul(T a,Args...b){return mul(a,mul(b...));}template<typename T1,typename T2>void muli(T1&a,T2 b){a=Mod(1ll*a*b);}template<typename T1,typename...Args>void muli(T1&a,Args...b){muli(a,mul(b...));} // /* trivial multiple function(idk whether its useful*/ int mul(int a, int b) { return 1ll * a * b % mod; } template <typename T1, typename T2> int mul(T1 a, T2 b) { return (long long)(1ll * a * b) % mod; } template <typename T, typename ...Args> int mul(T a, Args ...b) { return mul(a, mul(b...)); }
/* qp fac C */              template<typename T1,typename T2>T1 qp(T1 a,T2 b){T1 ret=1;for(;b>0;a=1ll*a*a%mod,b>>=1)if(b&1)ret=mul(ret,a);return ret;}vi __fac({1,1}),__ifc({1,1}),__inv({0,1});inline void ___prep(int n){static int i=2;if(i<n)for(__fac.resize(n),__ifc.resize(n),__inv.resize(n);i<n;i++)__fac[i]=mul(i,__fac[i-1]),__inv[i]=mul((mod-mod/i),__inv[mod%i]),__ifc[i]=mul(__inv[i],__ifc[i-1]);}inline int fac(int x){return ___prep(x+1),__fac[x];}inline int ifc(int x){return ___prep(x+1),__ifc[x];}inline int inv(int x){return ___prep(x+1),__inv[x];}inline int C(int n,int m){if(n<m or n<0 or m<0)return 0;return mul(fac(n),ifc(m),ifc(n-m));}
/* sum of i^k */            int S(int n,int k){vector<int>__pre(k+4),__suf(k+4),__pw(k+4),__pri(k+4);vector<bool>__vis(k+4,0);__pw[1]=1;for(int i=2,cnt=0;i<=k+2;++i){if(!__vis[i])__pri[++cnt]=i,__pw[i]=qp(i,k);for(int j=1;j<=cnt and i*__pri[j]<=k+2;++j){__vis[i*__pri[j]]=1;__pw[i*__pri[j]]=mul(__pw[i],__pw[__pri[j]]);if(i%__pri[j]==0)break;}}rep(i,2,k+2)__pw[i]=add(__pw[i],__pw[i-1]);__pre[0]=__suf[k+3]=1;rep(i,1,k+2)__pre[i]=mul(__pre[i-1],(n-i+mod));pre(i,k+2,1)__suf[i]=mul(__suf[i+1],(n-i+mod));int tmp=0,ret=0;rep(i,1,k+2){if((k-i)&1)subi(ret,mul(__pw[i],__pre[i-1],__suf[i+1],ifc(i-1),ifc(k+2-i)));else addi(ret,mul(__pw[i],__pre[i-1],__suf[i+1],ifc(i-1),ifc(k+2-i)));}return ret;}
// /* inv 2/3/6 / phi */ 		constexpr int __var_pow(int a, int b) { int ret = 1; for (; b > 0; b >>= 1, a = 1ll * a * a % mod) if (b & 1) ret = 1ll * ret * a % mod; return ret; } constexpr int __var_phi(int x) { if (x <= 2) return 1; int ret = x; for (int i = 2; 1ll * i * i <= x; ++i)  if (x % i == 0) { ret = ret / i * (i - 1); while (x % i == 0) x /= i; } if (x > 1) ret = ret / x * (x - 1); return ret; } const int __phi_mod = __var_phi(mod); const int inv2 = __var_pow(2, __phi_mod - 1), inv3 = __var_pow(3, __phi_mod - 1), inv6 = __var_pow(6, __phi_mod - 1);

int n;
int pos[N], now;
int vis[N];
int T[N * N], L[N * N], R[N * N], M[N * N], cnt;
int ans[N][N], f[N][N], g[N][N];

signed main() {
    file(gou);
    cin >> n >> mod; Mod.init(mod);
    rep(t,1,n) {
        pos[now = 0] = 0;
        rep(i,1,n) if (vis[i]) pos[++ now] = i;
        pos[++ now] = n + 1;
        int mx = 0, tme = 0, tot[2] = {0, 0};
        pre(i,now,1) pos[i] -= pos[i - 1];
        rep(i,1,now) {
            if (pos[i] / 2 > mx) mx = pos[i] / 2, tme = 1 + (pos[i] & 1), tot[0] = tot[1] = 0, tot[pos[i] & 1] ++;
            else if (pos[i] / 2 == mx) tme += 1 + (pos[i] & 1), tot[pos[i] & 1] ++;
        } int p = qp(tme, mod - 2);
        if (mx == 1) {
            rep(i,1,n) if (!vis[i]) ans[t][i] = p;
            continue;
        } rep(i,0,tot[0]) rep(j,0,tot[1]) f[i][j] = g[i][j] = 0;
        f[0][0] = g[0][0] = 1;
        rep(tt,0,tot[0]+tot[1]-1) {
            int sumf = 0, sumg = 0;
            rep(i,max(0,tt-tot[1]),min(tt,tot[0])) {
                int j = tt - i, num = tot[0] - i + 2 * (tot[1] - j);
                addi(sumf, mul(f[i][j], inv(num)));
                addi(sumg, mul(g[i][j], inv(num)));
                if (i < tot[0]) {
                    addi(f[i + 1][j], mul(f[i][j], tot[0] - i - 1, inv(num)));
                    addi(g[i + 1][j], mul(g[i][j], tot[0] - i, inv(num)));
                } if (j < tot[1]) {
                    addi(f[i][j + 1], mul(2, f[i][j], tot[1] - j, inv(num)));
                    addi(g[i][j + 1], mul(2, g[i][j], tot[1] - j - 1, inv(num)));
                } int sum = 0;
                for (int i = 1; i <= now; sum += pos[i ++]) if (pos[i] / 2 == mx) {
                    if (pos[i] & 1) {
                        ans[t + tt][sum + mx] = ans[t + tt][sum + mx + 1] = sumg;
                        T[++ cnt] = t + 1, L[cnt] = sum + 1, M[cnt] = sum + mx, R[cnt] = sum + pos[i] - 1;
                    } else ans[t + tt][sum + mx] = sumf;
                    vis[sum + mx] = 1;
                }
            }
        } t += tot[0] + tot[1] - 1;
    } 
    pre(i,cnt,1) rep(j,T[i],n) rep(k,L[i],M[i])
        ans[j][k] = ans[j][R[i] + L[i] - k] = mul(add(ans[j][k], ans[j][R[i] + L[i] - k]), inv(2));
    rep(i,1,n) {
        rep(j,1,n) cout << ans[i][j] << ' ';
        cout << endl;
    }
}



1.10 T2

对套路的讨论见 23.1.10 闲话

我们可以简单地写出 \(O(n^2)\) 的 dp。发现其按奇偶性分组后各自的转移是凸的。具体地,我们每次选择一个二元组,并且肯定贪心地按照从大到小的顺序选择,因此贡献是凸的。

因此可以进行一些讨论后对不同的情况维护四个凸包,分别进行合并即可。

code
#include <bits/stdc++.h>
using namespace std; 
#define int long long
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 5e5 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, a[N];

vi merge(vi a, vi b, bool nxt) {
    vi c(a.size() + b.size() - 1);
    c[0] = a[0] + b[0];
    merge(a.begin() + 1, a.end(), b.begin() + 1, b.end(), c.begin() + 1, greater<>() );
    if (nxt) c.emplace(c.begin(), 0);
    return c;
}

vi add(vi a, vi b) {
    rep(i,1,a.size()-1) a[i] += a[i - 1];
    rep(i,1,b.size()-1) b[i] += b[i - 1];
    if (a.size() > b.size()) swap(a, b);
    while (a.size() < b.size()) a.emplace_back(-1e18);
    rep(i,0,a.size()-1) a[i] = max(a[i], b[i]);
    pre(i,a.size()-1,1) a[i] -= a[i - 1];
    return a;
}

vector<vi> solve(int l, int r) {
    if (l == r) { return { { a[l] }, { - a[l] }, { 0 }, { 0 } } ; }
    int mid = l + r >> 1;
    auto L = solve(l, mid), R = solve(mid + 1, r);
    vector<vi> ret(4);
    ret[0] = add(merge(L[0], R[3], 0), merge(L[2], R[0], 0));
    ret[1] = add(merge(L[1], R[2], 0), merge(L[3], R[1], 0));
    ret[2] = add(merge(L[2], R[2], 0), merge(L[0], R[1], 1));
    ret[3] = add(merge(L[3], R[3], 0), merge(L[1], R[0], 1));
    return ret;
}

signed main() {
    file(jia); iot;
    cin >> n;
    rep(i,1,n) cin >> a[i];
    auto ans = solve(1, n);
    rep(i,1,ans[0].size()-1) ans[0][i] += ans[0][i - 1];
    rep(i,1,ans[2].size()-1) ans[2][i] += ans[2][i - 1];
    rep(i,1,n) cout << (i & 1 ? ans[0][i >> 1] : ans[2][i >> 1]) << ' ';
}



1.9 T2

第一眼想到了 LCT。

我们对链进行 DP,于是设计状态 \(f(u, i)\) 表示 \(u\) 子树内根节点所在链的长度为 \(i\) 情况下的最大深度。容易发现当 \(u\) 为叶子时只有 \(f(k, 1) = 1\)

然后枚举实子树(主要是左子树)的数量和大小。记录一个 LCT 上子树大小 \(siz(u)\),这个可以直接转移到原树上的深度。
根据 LCT 的二叉树性质可以得到答案。

code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
const int N = 5e3 + 10;
int n, siz[N], t1, t2; ll f[N][N], mx[N];
vector<int> g[N];
void dfs(int u) {
    siz[u] = 1; ll sum = 0;
    if (g[u].size() == 0) { f[u][1] = mx[u] = 1; return; }
    for (auto v : g[u]) 
        dfs(v), siz[u] += siz[v], sum += mx[v];
    f[u][1] = sum + siz[u];
    for (auto x : g[u]) rep(sizx,1,siz[x]) 
        f[u][sizx + 1] = f[x][sizx] + siz[u];
    for (auto x : g[u]) for (auto y : g[u]) if (x != y)
        rep(sizx,1,siz[x]) rep(sizy,1,siz[y]) 
            f[u][sizx + sizy + 1] = max(f[u][sizx + sizy + 1], f[x][sizx] + f[y][sizy] + sum - mx[x] - mx[y] + 1ll * (siz[u] - siz[x]) * (sizx + 1));
    mx[u] = *max_element(f[u] + 1, f[u] + siz[u] + 1);
}
signed main() {
    file(tree); iot;
    cin >> n;
    rep(i,2,n) cin >> t1, g[t1].eb(i);
    memset(f, -127, sizeof f);
    dfs(1);
    cout << mx[1] << '\n';
}



1.9 T3

数据结构。

考虑排序操作,我们不如使用一个有序的结构保存一段的信息,采用颜色段均摊的手法维护这个序列。内层采用可分裂/合并的平衡树,外层采用珂朵莉树结构可以做到均摊的 \(O(n \log n)\)
现在我们需要支持区间位运算,这启发我们使用 01Trie 维护这个结构。01Trie 的分裂(按值分裂)与合并也是均摊 \(O(\log n)\) 的,证法和上面的相同。位运算可以采用三个 lazy 标记维护,能直接压进三个 word。异或是交换子树,与/或是将两棵子树合并入一棵子树,应用上面的合并函数即可。这样外面采用手写的珂树来维护 lazy 标记能做到时间复杂度均摊 \(O(n \log n)\)

均摊复杂度详见 TEST_152 ,具体做法详见 Ynoi。码量不小。

code
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 1e5 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, m, ccnt;
unsigned int v, a[N], ans[N];
int typ, l, r, L, M, R;
int tmp;

struct node {
    unsigned int x[2], rev;
    node(unsigned int a = 0, unsigned int b = 0, unsigned int c = 0) { x[0] = a, x[1] = b, rev = c; }
    bool empty() { return !x[0] and !x[1] and !rev; }
    void clear() { x[0] = x[1] = rev = 0; }
    void operator() (unsigned int &v) { v &= ~x[0], v |= x[1], v ^= rev; }
    void operator+= (const node& b) {
        rep(i,0,1) x[i] |= b.x[i], x[!i] &= ~b.x[i], rev &= ~b.x[i];
        unsigned int v = (x[0] & b.rev) | (x[1] & b.rev);
        x[0] ^= v, x[1] ^= v;
        rev ^= b.rev ^ v;
    }
} trp[N], trie[N], tag[N * 100];

namespace Trie {
    void merge(int&,int,int,int);
    void ps_d(int,int);

    int ch[N * 100][2], cnt[N * 100], mlc;
    #define ls(p) ch[p][0]
    #define rs(p) ch[p][1]

    void ins(int &rt, unsigned int x) {
        if (!rt) rt = ++ mlc;
        int p = rt;
        for (signed i = 31; i >= 0; -- i) {
            ++ cnt[p];
            if (!ch[p][x >> i & 1]) 
                ch[p][x >> i & 1] = ++ mlc;
            p = ch[p][x >> i & 1];
        } ++ cnt[p];
    }

    void ps_d(int p, int d) {
        if (d < 0 or tag[p].empty()) return;
        if (tag[p].x[0] >> d & 1) merge(ls(p), ls(p), rs(p), d - 1), rs(p) = 0; // and
        else if (tag[p].x[1] >> d & 1) merge(rs(p), rs(p), ls(p), d - 1), ls(p) = 0; // or
        else if (tag[p].rev >> d & 1) swap(ls(p), rs(p));
        if (ls(p)) tag[ls(p)] += tag[p];
        if (rs(p)) tag[rs(p)] += tag[p];
        tag[p].clear();
    }

    void merge(int &p, int x, int y, int d) {
        if (!x or !y) return p = x + y, void();
        ps_d(x, d), ps_d(y, d), cnt[p = x] += cnt[y];
        merge(ls(p), ls(x), ls(y), d - 1);
        merge(rs(p), rs(x), rs(y), d - 1);
    }

    void split(int x, int &y, int k, int d) {
        y = ++ mlc;
        if (d == -1) { cnt[y] = cnt[x] - k, cnt[x] = k; return; }
        ps_d(x, d); int siz = cnt[ls(x)];
        if (k > siz) split(rs(x), rs(y), k - siz, d - 1);
        else swap(rs(x), rs(y));
        if (k < siz) split(ls(x), ls(y), k, d - 1);
        cnt[y] = cnt[x] - k, cnt[x] = k;
    }

    void calcans(int p, unsigned int x, signed d, node lzy) {
        if (d == -1) { lzy(x); while (cnt[p] --) ans[++ ccnt] = x; return; }
        ps_d(p, d);
        if (ls(p)) calcans(ls(p), x, d - 1, lzy);
        if (rs(p)) calcans(rs(p), x | (1ll << d), d - 1, lzy);
    }

    #undef ls
    #undef rs
} // namespace yist

namespace Treap {
    #define ls(p) treap[p].ls 
    #define rs(p) treap[p].rs
    #define rt(p) treap[p].rt 
    #define siz(p) treap[p].siz 
    #define rnd(p) treap[p].rnd 

    int rt, rec[N];
    struct TrNode {
        int ls, rs, siz;
        int rnd, rt;
    } treap[N];

    int New(int rrt) {
        int p = rec[(*rec) --];
        siz(p) = Trie::cnt[rrt], rnd(p) = rand(), rt(p) = rrt;
        return p;
    }

    void ps_p(int p) { siz(p) = siz(ls(p)) + siz(rs(p)) + Trie::cnt[rt(p)]; }
    void ps_d(int p) {
        if (trp[p].empty()) return;
        if (ls(p)) trp[ls(p)] += trp[p], trie[ls(p)] += trp[p];
        if (rs(p)) trp[rs(p)] += trp[p], trie[rs(p)] += trp[p];
        trp[p].clear();
    }

    void merge(int &p, int x, int y) {
        if (!x or !y) return p = x + y, void();
        if (rnd(x) < rnd(y)) ps_d(p = x), merge(rs(p), rs(x), y);
        else ps_d(p = y), merge(ls(p), x, ls(y));
        ps_p(p);
    }

    void split(int p, int &x, int &y, int k) {
        if (!p) return x = y = 0, void();
        ps_d(p);
        if (k <= siz(ls(p))) y = p, split(ls(p), x, ls(y), k);
        else if (k >= siz(ls(p)) + Trie::cnt[rt(p)]) 
            x = p, split(rs(p), rs(x), y, k - siz(ls(p)) - Trie::cnt[rt(p)]);
        else {
            Trie::split(rt(p), tmp, k - siz(ls(p)), 31);
            x = p, y = New(tmp), rs(y) = rs(p), rs(p) = 0;
            ps_p(y); trie[y] = trie[p];
        } ps_p(p);
    }

    void dfs(int &p) {
        if (!p) return ;
        ps_d(p), tag[rt(p)] += trie[p], trie[p].clear();
        Trie::merge(tmp, tmp, rt(p), 31); rt(p) = 0;
        dfs(ls(p)), dfs(rs(p)), rec[++*rec] = p, p = 0;
    }

    void calcans(int p = rt) {
        if (!p) return;
        ps_d(p);
        calcans(ls(p));
        Trie::calcans(rt(p), 0, 31, trie[p]);
        calcans(rs(p));
    }
} // namespace ernd

signed main() {
	file(sort); iot;
    cin >> n >> m; 
    Treap::rec[0] = n;
    rep(i,1,n) Treap::rec[i] = n - i + 1;
    rep(i,1,n) {
        cin >> a[i];
        Trie::ins(tmp = 0, a[i]);
        Treap::merge(Treap::rt, Treap::rt, Treap::New(tmp));
    } 
    while (m --) {
        cin >> typ >> l >> r;
        Treap::split(Treap::rt, L, R, r);
        Treap::split(L, L, M, l - 1);
        if (typ == 1) {
            cin >> v;
            trp[M] += node(0, v, 0), trie[M] += node(0, v, 0);
            Treap::merge(L, L, M);
            Treap::merge(Treap::rt, L, R);
        } else if (typ == 2) {
            cin >> v;
            trp[M] += node(~v, 0, 0), trie[M] += node(~v, 0, 0);
            Treap::merge(L, L, M);
            Treap::merge(Treap::rt, L, R);
        } else if (typ == 3) {
            cin >> v;
            trp[M] += node(0, 0, v), trie[M] += node(0, 0, v);
            Treap::merge(L, L, M);
            Treap::merge(Treap::rt, L, R);
        } else if (typ == 4) {
            tmp = 0; Treap::dfs(M);
            Treap::merge(L, L, Treap::New(tmp));
            Treap::merge(Treap::rt, L, R);
        } 
    } Treap::calcans();
    rep(i,1,n) cout << ans[i] << ' '; cout << endl;
}



1.6 T3

回文类的题考虑枚举回文中心,然后并行地对两边 dp。

考虑一个区间 dp,设 \(f([l, r], k)\) 为目前考虑了 \([l,r]\) 段,当前的回文串左边多了 \(k\in \mathbb Z\) 个字符的情况下种类数。然后对每个字符串求哈希使得可以 \(O(1)\) 匹配,随后遍历状态即可。作前缀和可以达到 \(O(n\sum |S_i|)\)

没实现出来。



1.6 T4

二分答案转化为判定,于是就成了覆盖问题,设当前答案为 \(mid\)。最终答案肯定不会超过两点的最大值,因此可以首先将最大值作为 \(a_n\to a_1\) 的段,断环成链后最后讨论。
\(1\) 在链最左侧,\(n\) 在最右侧。

对于链的部分,可以贪心地考虑。如果 \(f_{i - 1} \ge a_i\),则 \(i\) 点向右贡献,反之向左贡献。向右贡献时最大值易求,向左时能覆盖到 \(i\) 右边的只有 \(i - 2\),这时如果 \(f_{i - 2} \ge a_i - mid\),我们就能让 \(i - 1\) 向右,覆盖的最大位置是 \(f_{i - 1} + mid\)

然后讨论断开的部分。这部分就是确定初值,看 \(1\) 向哪边。分别讨论。
如果向左,可以直接按照链求解。
反之我们需要填补 \(n\to 1\) 的部分,必须让 \(2\) 向左覆盖。经过对 \(1\) 覆盖范围的讨论可以发现 \(3\) 覆盖 \(n\to 1\) 一定是不优的,因此链从 \(3\) 开始 dp 即可。

code
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define multi int T; cin >> T; while ( T -- )
template <typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
template <typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define ufile(x) 
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define Aster(i, s) for (int i = head[s], v; i; i = e[i].next)
#define all(s) s.begin(), s.end()
#define eb emplace_back
#define pb pop_back
#define em emplace
const int N = 1e6 + 10;
int m, n, a[N], f[N];

bool checkl(int len) {
	f[1] = a[1];
	rep(i,2,n) {
		if (f[i - 1] >= a[i] - 1) f[i] = a[i] + len;
		else if (f[i - 1] >= a[i] - len - 1) {
			if (f[i - 2] >= a[i - 1] - len - 1) 
				f[i] = max(a[i], a[i - 1] + len);
			else f[i] = a[i];
		} else return false;
	} return f[n] >= a[1] + m - 1 - len;
}
bool checkr(int len) {
	f[1] = a[1] + len;
	f[2] = max(f[1], a[2]);
	if (f[1] + 1 < a[2]) return false;
	rep(i,3,n) {
		if (f[i - 1] >= a[i] - 1) f[i] = a[i] + len;
		else if (f[i - 1] >= a[i] - len - 1) {
			if (f[i - 2] >= a[i - 1] - len - 1) 
				f[i] = max(a[i], a[i - 1] + len);
			else f[i] = a[i];
		} else return false;
	} return f[n] >= min(a[1], a[2] - len) + m - 1;
}

signed main() {
	file(expansion);
    cin >> m >> n;
	if (n == 1) {
		cout << m - 1 << '\n';
		return 0;
	} rep(i,1,n) cin >> a[i];
	sort(a + 1, a + 1 + n);
	int mx = a[1] + m - a[n], pos = a[n];
	rep(i,1,n) if (mx < a[i + 1] - a[i]) {
		mx = a[i + 1] - a[i];
		pos = a[i];
	} rep(i,1,n) a[i] = (a[i] - pos - 1 + m) % m + 1;
	sort(a + 1, a + 1 + n);
	int l = 0, r = mx - 1, mid;
	while (l <= r) {
		mid = l + r >> 1;
		if (checkl(mid) or checkr(mid)) r = mid - 1;
		else l = mid + 1;
	} cout << r + 1 << '\n';
}
posted @ 2023-01-11 18:48  joke3579  阅读(82)  评论(0)    收藏  举报