[BJOI2019]奥术神杖
容易发现这是一个
A
C
AC
AC 自动机带
d
p
dp
dp 的比较套路的题
现在的问题就是最大化一个根号下面的东西
考虑乘法变加法去个
l
n
ln
ln,
l
n
(
∏
i
=
1
k
a
i
)
1
k
=
1
k
∑
i
=
1
k
l
n
(
a
i
)
ln (\prod_{i=1}^k a_i)^{\frac{1}{k}}=\frac{1}{k}\sum_{i=1}^kln(a_i)
ln(∏i=1kai)k1=k1∑i=1kln(ai)
然后就是最大化后面那个式子,分数规划即可
#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]勘破神机
很妙的数学题
首先
2
∗
n
2*n
2∗n 的情况就是斐波那契
于是,令
f
i
f_i
fi 为斐波那契数列
a
n
s
=
∑
i
=
l
r
(
f
i
k
)
ans=\sum_{i=l}^r\binom{f_i}{k}
ans=i=l∑r(kfi)
考虑到
1
k
!
\frac{1}{k!}
k!1 是一个定值,而开出去过后是一个下降幂,是不是可以用斯特林化简
a
n
s
=
∑
i
=
l
r
(
f
i
k
)
ans=\sum_{i=l}^r\binom{f_i}{k}
ans=i=l∑r(kfi)
=
1
k
!
∑
i
=
l
r
∑
j
=
0
k
(
−
1
)
k
−
j
∗
s
k
,
j
∗
f
i
j
=\frac{1}{k!}\sum_{i=l}^r \sum_{j=0}^k (-1)^{k-j}*s_{k,j}*f_i^j
=k!1i=l∑rj=0∑k(−1)k−j∗sk,j∗fij
考虑到
f
n
=
1
5
(
1
+
5
2
)
n
−
1
5
(
1
−
5
2
)
n
f_n=\frac{1}{\sqrt 5}(\frac{1+\sqrt 5}{2})^n-\frac{1}{\sqrt 5}(\frac{1-\sqrt 5}{2})^n
fn=51(21+5)n−51(21−5)n
令
f
n
=
A
x
n
+
B
y
n
f_n=Ax^n+By^n
fn=Axn+Byn,那么原式为
=
1
k
!
∑
i
=
l
r
∑
k
=
0
j
(
−
1
)
k
−
j
∗
s
k
,
j
∗
(
A
x
i
+
B
y
i
)
j
=\frac{1}{k!}\sum_{i=l}^r\sum_{k=0}^j(-1)^{k-j}*s_{k,j}*(Ax^i+By^i)^j
=k!1i=l∑rk=0∑j(−1)k−j∗sk,j∗(Axi+Byi)j
=
1
k
!
∑
k
=
0
j
(
−
1
)
k
−
j
∗
s
k
,
j
∗
∑
i
=
l
r
(
A
x
i
+
B
y
i
)
j
=\frac{1}{k!}\sum_{k=0}^j(-1)^{k-j}*s_{k,j}*\sum_{i=l}^r(Ax^i+By^i)^j
=k!1k=0∑j(−1)k−j∗sk,j∗i=l∑r(Axi+Byi)j
暴力展开
=
1
k
!
∑
k
=
0
j
(
−
1
)
k
−
j
∗
s
k
,
j
∗
∑
i
=
l
r
∑
l
=
0
j
(
j
l
)
(
A
∗
x
i
)
l
∗
(
B
∗
y
i
)
j
−
l
=\frac{1}{k!}\sum_{k=0}^j(-1)^{k-j}*s_{k,j}*\sum_{i=l}^r\sum_{l=0}^j\binom{j}{l} (A*x^i)^l*(B*y^i)^{j-l}
=k!1k=0∑j(−1)k−j∗sk,j∗i=l∑rl=0∑j(lj)(A∗xi)l∗(B∗yi)j−l
=
1
k
!
∑
k
=
0
j
(
−
1
)
k
−
j
∗
s
k
,
j
∗
∑
l
=
0
j
(
j
l
)
A
l
B
j
−
l
∑
i
=
l
r
x
i
l
∗
y
i
∗
(
j
−
l
)
=\frac{1}{k!}\sum_{k=0}^j(-1)^{k-j}*s_{k,j}*\sum_{l=0}^j\binom{j}{l} A^lB^{j-l}\sum_{i=l}^r x^{il}*y^{i*(j-l)}
=k!1k=0∑j(−1)k−j∗sk,j∗l=0∑j(lj)AlBj−li=l∑rxil∗yi∗(j−l)
发现后面一坨就是
∑
i
=
l
r
x
i
l
∗
y
i
∗
(
j
−
l
)
=
∑
(
x
l
y
j
−
l
)
i
\sum_{i=l}^r x^{il}*y^{i*(j-l)}=\sum (x^ly^{j-l})^i
i=l∑rxil∗yi∗(j−l)=∑(xlyj−l)i
等比数列
到这里复杂度是
O
(
n
2
l
o
g
(
n
)
)
O(n^2log(n))
O(n2log(n)) 的
5
\sqrt 5
5 直接带复数运算
考虑 3 个的情况,显然只有偶数的时候有答案,于是我们每两列分一块
首先由
f
i
=
f
i
−
1
∗
3
f_i=f_{i-1}*3
fi=fi−1∗3
然后还有跨块的情况
方案数是
2
∗
∑
j
=
0
i
−
2
f
i
2*\sum_{j=0}^{i-2} f_{i}
2∗∑j=0i−2fi
不想写了,把
n
+
1
n+1
n+1 写出来,做差,解特征方程,求通项就和上面一样了
#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]排兵布阵
按
a
i
a_i
ai 排序后
d
p
dp
dp 方程显然
f
j
=
m
a
x
(
f
j
,
f
j
−
2
∗
a
i
,
k
−
1
+
i
k
)
f_j=max(f_j,f_{j-2*a_{i,k}-1}+ik)
fj=max(fj,fj−2∗ai,k−1+ik)
[BJOI2019]光线
f
,
g
f,g
f,g 分别表示向下向上穿透的概率
f
i
=
f
i
−
1
∗
a
i
+
g
i
+
1
∗
b
i
f_i=f_{i-1}*a_i+g_{i+1}*b_i
fi=fi−1∗ai+gi+1∗bi
g
i
=
f
i
−
1
∗
b
i
+
g
i
+
1
∗
a
i
g_i=f_{i-1}*b_i+g_{i+1}*a_i
gi=fi−1∗bi+gi+1∗ai
f
n
=
f
n
−
1
∗
a
n
f_n=f_{n-1}*a_n
fn=fn−1∗an
g
n
=
f
n
−
1
∗
b
n
g_n=f_{n-1}*b_n
gn=fn−1∗bn
这个时候会发现非常的绞,这也是这道题的难点,考虑从后两个式子寻找突破口
发现
f
n
f_n
fn 可以用
f
n
−
1
f_{n-1}
fn−1 表示,
g
n
g_n
gn 可以用
f
n
−
1
f_{n-1}
fn−1 表示
这启示我们继续考虑
n
−
1
n-1
n−1
发现
f
n
−
1
=
f
n
−
2
∗
a
n
−
1
+
(
f
n
−
1
∗
b
n
)
∗
a
n
−
1
f_{n-1}=f_{n-2}*a_{n-1}+(f_{n-1}*b_n)*a_{n-1}
fn−1=fn−2∗an−1+(fn−1∗bn)∗an−1
也就是说
f
n
−
1
f_{n-1}
fn−1 可以用
f
n
−
2
f_{n-2}
fn−2 表示
于是我们就可以待定系数,先
f
i
f_i
fi 为多少倍的
f
i
−
1
f_{i-1}
fi−1,以及
g
i
g_i
gi 是多少倍的
f
i
−
1
f_{i-1}
fi−1
最后从头推回去,感觉有点妙
#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]删数
发现顺序不影响,我们在脑中给原序列排一个序
手玩一波
{
1
,
2
,
2
,
3
,
3
,
3
}
\{1,2,2,3,3,3\}
{1,2,2,3,3,3},显然最优方案是
{
3
,
3
,
3
,
6
,
6
,
6
}
\{3,3,3,6,6,6\}
{3,3,3,6,6,6}
发现一个巧妙的性质,如果我们在 6 这个位置放一个高为 3 的柱子,在 3 这个位置放一个 高为 3 的柱子
那么向左推到刚好覆盖原序列
那么我们模拟一下把原序列推倒的情况,位置1覆盖了 3 次,位置 2 覆盖了 2 次,而 4,5,6 都没有覆盖
显然需要把重复的
k
k
k 个覆盖挪到空格上,发现铁定构造地出来
另外,可以很容易的发现,这种构造方法恰是方案数的下界
于是问题转换为求 0 的个数,线段树维护最小值即最小值个数即可
一个单点修改把变动的区间修改即可,整体 + 1 有些头疼
考虑整体+1 有什么变化
{
1
,
2
,
2
,
3
,
3
,
3
}
−
−
−
{
2
,
3
,
3
,
4
,
4
,
4
}
\{1,2,2,3,3,3\}--- \{2,3,3,4,4,4\}
{1,2,2,3,3,3}−−−{2,3,3,4,4,4}
覆盖区间的变化
[
1
,
1
]
,
[
1
,
2
]
,
[
1
,
3
]
−
−
−
[
2
,
2
]
,
[
2
,
3
]
,
[
2
,
4
]
[1,1],[1,2],[1,3]---[2,2],[2,3],[2,4]
[1,1],[1,2],[1,3]−−−[2,2],[2,3],[2,4],全部挪了一位
现在查询
[
1
,
6
]
[1,6]
[1,6] 的答案相当于在原来查询
[
0
,
5
]
[0,5]
[0,5] 的答案,那么记一个挪的位就可以了
#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;
}