闲话 23.1.11
今日推歌:《君と見た星空》 by EasyPop feat. 巡音ルカ
挺好听的一首歌
我对今天 T3 的评价

来源是哪里呢?
杂题
挑几道最近模拟赛做到的好玩题写写。有点感性化?
1.11 T2
你看这数据范围也没想法了,要么是模拟费用流要么是 DP 转生成函数。这里是 DP。
观察可以发现行列无关,因此我们可以行列分开计数。假设最终有 \(i\) 行被恰好操作奇数次的方案是 \(f_i\),对列是 \(g_i\),则答案即为
可以发现对于一个确定的 \(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\),而它们的生成函数是易求的。随后只需要选择具体的行,直接写出:
我们已经完成最抽象的第一步了,下面就是代数工业的活了。
随后我们就可以采用多项式计算了。对于
可以先计算
的前 \(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';
}
以下是博客签名,与正文无关。
请按如下方式引用此页:
本文作者 joke3579,原文链接:https://www.cnblogs.com/joke3579/p/chitchat230111.html。
遵循 CC BY-NC-SA 4.0 协议。
请读者尽量不要在评论区发布与博客内文完全无关的评论,视情况可能删除。

浙公网安备 33010602011771号