​[BJOI2019]奥术神杖​​​ 容易发现这是一个 BJOI 2019 题解_#define 自动机带 BJOI 2019 题解_i++_02 的比较套路的题
现在的问题就是最大化一个根号下面的东西
考虑乘法变加法去个 BJOI 2019 题解_i++_03BJOI 2019 题解_c++_04
然后就是最大化后面那个式子,分数规划即可

#include<bits/stdc++.h>
#define N 1550
#define eps 1e-3
using namespace std;
int n, m, res;
int ch[N][10], fail[N], siz[N], tot, g[N][N], h[N][N];
double f[N][N], w[N];
char T[N], ans[N];
void Insert(string s, int v){
int now = 0, len = s.length();
for(int i=0; i<len; i++){
int x = s[i] - '0';
if(!ch[now][x]) ch[now][x] = ++tot;
now = ch[now][x];
} w[now] = log(v), siz[now]++;
}
void Build(){
queue<int> q;
for(int i=0; i<10; i++)
if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty()){
int x = q.front(); q.pop();
w[x] += w[fail[x]], siz[x] += siz[fail[x]];
for(int i=0; i<10; i++){
if(ch[x][i]) fail[ch[x][i]] = ch[fail[x]][i], q.push(ch[x][i]);
else ch[x][i] = ch[fail[x]][i];
}
}
}
void Modify(int i, int j, int k){
int c = ch[j][k];
if(f[i][c] < f[i-1][j] + w[c]) f[i][c] = f[i-1][j] + w[c], g[i][c] = j, h[i][c] = k;
}
bool check(double mid){
for(int i=0; i<=tot; i++) w[i] -= mid * siz[i];
for(int i=0; i<=n; i++)
for(int j=0; j<=tot; j++)
f[i][j] = - 1e17;
f[0][0] = 0;
for(int i=1; i<=n; i++){
for(int j=0; j<=tot; j++){
if(T[i] == '.') for(int k=0; k<10; k++) Modify(i, j, k);
else Modify(i, j, T[i]-'0');
}
}
double ans = - 1e17;
for(int i=0; i<=tot; i++) ans = max(ans, f[n][i]);
for(int i=0; i<=tot; i++) w[i] += mid * siz[i];
return ans > 0;
}
void dfs(int i, int j){ if(i == 0) return; dfs(i-1, g[i][j]); cout<<h[i][j];}
int main(){
scanf("%d%d", &n, &m); res = n;
scanf("%s", T+1);
for(int i=1; i<=m; i++){
string s; int x; cin >> s >> x;
Insert(s, x);
} Build();
double l = 0, r = 25;
while((r-l) > eps){
double mid = (l+r)/2;
if(check(mid)) l = mid;
else r = mid;
} check(l); double mx = -1e17; int pos = 0;
for(int i=0; i<=tot; i++) if(f[n][i] > mx) mx = f[n][i], pos = i;
dfs(n, pos); return 0;
}

​[BJOI2019]勘破神机​​​ 很妙的数学题
首先 BJOI 2019 题解_c++_05 的情况就是斐波那契
于是,令 BJOI 2019 题解_i++_06 为斐波那契数列
BJOI 2019 题解_#define_07
考虑到 BJOI 2019 题解_#define_08 是一个定值,而开出去过后是一个下降幂,是不是可以用斯特林化简
BJOI 2019 题解_#define_07
BJOI 2019 题解_i++_10
考虑到 BJOI 2019 题解_i++_11
BJOI 2019 题解_i++_12,那么原式为
BJOI 2019 题解_#define_13
BJOI 2019 题解_i++_14
暴力展开
BJOI 2019 题解_i++_15
BJOI 2019 题解_i++_16
发现后面一坨就是
BJOI 2019 题解_c++_17
等比数列
到这里复杂度是 BJOI 2019 题解_#define_18
BJOI 2019 题解_#define_19 直接带复数运算

考虑 3 个的情况,显然只有偶数的时候有答案,于是我们每两列分一块
首先由 BJOI 2019 题解_#define_20
然后还有跨块的情况
方案数是 BJOI 2019 题解_c++_21
不想写了,把 BJOI 2019 题解_c++_22 写出来,做差,解特征方程,求通项就和上面一样了

#include<bits/stdc++.h>
#define N 505
using namespace std;
typedef long long ll;
const int Mod = 998244353, inv2 = (Mod+1)/2;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod;}
int power(int a, int b){int ans=1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a); return ans;}
int inv(int x){ return power(x, Mod-2);}
const int inv5 = inv(5), inv6 = inv(6);
int T, m;
ll l, r; int k;
int c[N + 10][N + 10], s[N + 10][N + 10], fac[N + 10];

#define cs const
struct data{
int x, y;
data(int _x = 0, int _y = 0){ x = _x; y = _y;}
data operator + (cs data &a){ return data(add(x, a.x), add(y, a.y));}
data operator - (cs data &a){ return data(add(x, Mod-a.x), add(y, Mod-a.y));}
data operator * (cs data &a){ return data(add(mul(x,a.x), mul(m, mul(y,a.y))), add(mul(x, a.y), mul(y, a.x)));}
data operator * (cs int &a){ return data(mul(x,a), mul(y,a));}
}X, Y, A, B;
data Inv(data a){ return data(a.x, Mod-a.y) * inv(add(mul(a.x,a.x), Mod-mul(m, mul(a.y,a.y))));}
data ksm(data a, ll b){ data ans(1, 0); for(;b;b>>=1,a=a*a) if(b&1) ans=ans*a; return ans;}

void prework(){
c[0][0] = 1;
for(int i = 1; i <= N ; i++){
c[i][0] = 1; for(int j = 1; j <= i; j++) c[i][j] = add(c[i-1][j-1], c[i-1][j]);
}
s[0][0] = 1;
for(int i = 1; i <= N; i++){
for(int j = 1; j <= i; j++) s[i][j] = add(s[i-1][j-1], mul(s[i-1][j], i-1));
}
fac[0] = fac[1] = 1;
for(int i = 1; i <= N; i++) fac[i] = mul(fac[i-1], i);
}
void FSY(){
l++,r++;
X = data(inv2, inv2); Y = data(inv2, Mod - inv2);
A = data(0, inv5); B = data(0, Mod - inv5);
int ans = 0;
for(int i = 0; i <= k; i++){
int now = ((k-i)&1) ? Mod-s[k][i] : s[k][i];
data res(0, 0);
for(int j = 0; j <= i; j++){
data tmp = ksm(X, j) * ksm(Y, i-j);
data val = (ksm(tmp, r+1) - ksm(tmp, l)) * Inv(tmp - data(1,0));
if(tmp.x == 1 && tmp.y == 0) val = tmp * ((r-l+1) % Mod);
res = res + ((ksm(A, j) * ksm(B, i-j) * val) * c[i][j]);
} ans = add(ans, mul(now, res.x));
} cout << mul(inv((r-l+1) % Mod), mul(inv(fac[k]), ans)) << '\n';
}
void Yolanda(){
int Yol = inv((r-l+1) % Mod);
l = (l + 1) >> 1, r = r >> 1;
X = data(2, 1); Y = data(2, Mod-1);
A = data(inv2, inv6); B = data(inv2, Mod - inv6);
int ans = 0;
for(int i = 0; i <= k; i++){
int now = ((k-i)&1) ? Mod-s[k][i] : s[k][i];
data res(0, 0);
for(int j = 0; j <= i; j++){
data tmp = ksm(X, j) * ksm(Y, i-j);
data val = (ksm(tmp, r+1) - ksm(tmp, l)) * Inv(tmp - data(1,0));
if(tmp.x == 1 && tmp.y == 0) val = tmp * ((r-l+1) % Mod);
res = res + ((ksm(A, j) * ksm(B, i-j) * val) * c[i][j]);
} ans = add(ans, mul(now, res.x));
} cout << mul(Yol, mul(inv(fac[k]), ans)) << '\n';
}
int main(){
prework();
scanf("%d%d", &T, &m);
if(m == 2) m = 5;
while(T--){
scanf("%lld%lld%d", &l, &r, &k);
if(m == 5) FSY(); else Yolanda();
}
}

​[BJOI2019]排兵布阵​​​ 按 BJOI 2019 题解_c++_23 排序后 BJOI 2019 题解_i++_02 方程显然
BJOI 2019 题解_i++_25

​[BJOI2019]光线​​​BJOI 2019 题解_#define_26 分别表示向下向上穿透的概率
BJOI 2019 题解_i++_27
BJOI 2019 题解_c++_28
BJOI 2019 题解_c++_29
BJOI 2019 题解_c++_30
这个时候会发现非常的绞,这也是这道题的难点,考虑从后两个式子寻找突破口
发现 BJOI 2019 题解_#define_31 可以用 BJOI 2019 题解_c++_32 表示,BJOI 2019 题解_i++_33 可以用 BJOI 2019 题解_c++_32 表示
这启示我们继续考虑 BJOI 2019 题解_i++_35
发现 BJOI 2019 题解_#define_36
也就是说 BJOI 2019 题解_c++_32 可以用 BJOI 2019 题解_i++_38 表示
于是我们就可以待定系数,先 BJOI 2019 题解_i++_06 为多少倍的 BJOI 2019 题解_i++_40,以及 BJOI 2019 题解_i++_41 是多少倍的 BJOI 2019 题解_i++_40
最后从头推回去,感觉有点妙

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a); return ans; }
cs int inv = ksm(100, Mod - 2);
cs int N = 5e5 + 5;
int a[N], b[N], f[N], g[N], n;
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d%d", &a[i], &b[i]);
a[i] = mul(a[i], inv);
b[i] = mul(b[i], inv);
}
for(int i = n; i >= 1; i--){
f[i] = mul(a[i], ksm(Mod+1-mul(g[i+1],b[i]), Mod-2));
g[i] = add(b[i], mul(f[i],mul(g[i+1],a[i])));
}
for(int i = 2; i <= n; i++) f[i] = mul(f[i], f[i-1]);
cout << f[n];
return 0;
}

​[BJOI2019]删数​​​ 发现顺序不影响,我们在脑中给原序列排一个序
手玩一波 BJOI 2019 题解_c++_43,显然最优方案是 BJOI 2019 题解_c++_44
发现一个巧妙的性质,如果我们在 6 这个位置放一个高为 3 的柱子,在 3 这个位置放一个 高为 3 的柱子
那么向左推到刚好覆盖原序列
那么我们模拟一下把原序列推倒的情况,位置1覆盖了 3 次,位置 2 覆盖了 2 次,而 4,5,6 都没有覆盖
显然需要把重复的 BJOI 2019 题解_#define_45 个覆盖挪到空格上,发现铁定构造地出来
另外,可以很容易的发现,这种构造方法恰是方案数的下界
于是问题转换为求 0 的个数,线段树维护最小值即最小值个数即可
一个单点修改把变动的区间修改即可,整体 + 1 有些头疼
考虑整体+1 有什么变化
BJOI 2019 题解_c++_46
覆盖区间的变化
BJOI 2019 题解_i++_47,全部挪了一位
现在查询 BJOI 2019 题解_#define_48 的答案相当于在原来查询 BJOI 2019 题解_i++_49 的答案,那么记一个挪的位就可以了

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch=='-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
cs int N = 150050, M = N * 3;
cs int up = 450005;
int n, m, a[N], len;
int c[M]; // 出现次数
namespace seg{
int mi[M << 2], ct[M << 2], tg[M << 2];
#define mid ((l+r)>>1)
void pushup(int x){
if(mi[x<<1] < mi[x<<1|1]) mi[x] = mi[x<<1], ct[x] = ct[x<<1];
if(mi[x<<1] == mi[x<<1|1]) mi[x] = mi[x<<1], ct[x] = ct[x<<1] + ct[x<<1|1];
if(mi[x<<1] > mi[x<<1|1]) mi[x] = mi[x<<1|1], ct[x] = ct[x<<1|1];
}
void build(int x, int l, int r){
if(l == r){ ct[x] = 1; return; }
build(x<<1, l, mid); build(x<<1|1, mid+1, r);
pushup(x);
}
void pushnow(int x, int v){ mi[x] += v; tg[x] += v; }
void pushdown(int x){
if(tg[x]) pushnow(x<<1, tg[x]), pushnow(x<<1|1, tg[x]), tg[x] = 0;
}
void modify(int x, int l, int r, int L, int R, int v){
if(L<=l && r<=R){ pushnow(x, v); return; }
pushdown(x);
if(L<=mid) modify(x<<1, l, mid, L, R, v);
if(R>mid) modify(x<<1|1, mid+1, r, L, R, v);
pushup(x);
}
int query(int x, int l, int r, int L, int R){
if(L<=l && r<=R){
return mi[x] == 0 ? ct[x] : 0;
}
pushdown(x); int ans = 0;
if(L<=mid) ans += query(x<<1, l, mid, L, R);
if(R>mid) ans += query(x<<1|1, mid+1, r, L, R);
return ans;
}
}
void modify(int x, int v){
int k = x - c[x] + 1 - (v > 0);
seg::modify(1, 1, up, k, k, v);
c[x] += v;
}
int main(){
n = read(), m = read();
len = 150001; seg::build(1, 1, up);
for(int i = 1; i <= n; i++){
a[i] = read(); a[i] += len;
modify(a[i], 1);
}
while(m--){
int p = read(), x = read();
if(p == 0){
if(x > 0){
int pos = len + n;
if(c[pos]) seg::modify(1, 1, up, pos - c[pos] + 1, pos, -1);
--len;
}
else{
++len;
int pos = len + n;
if(c[pos]) seg::modify(1, 1, up, pos - c[pos] + 1, pos, 1);
}
}
else{
if(a[p] <= len + n) modify(a[p], -1);
else --c[a[p]];
a[p] = len + x;
if(a[p] <= len + n) modify(a[p], 1);
else ++c[a[p]];
} cout << seg::query(1, 1, up, len + 1, len + n) << '\n';
} return 0;
}