不会,二分/三分,期望dp + 高斯消元
# A. 记事本
不会。
:建图跑最短路。
:分讨模拟。
放个 60 分的代码吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
using namespace std;
namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
template <typename T> inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const int N = 2010;
int n, q;
int a[N], pre[N], nxt[N];
int mn[N][20];
inline int qry(int l, int r){
if(l >= r) return 1e9;
int k = log2(r - l + 1);
// cout << l << " " << r << " " << k << " " << r - (1 << k) + 1 << endl;
return min(mn[l][k], mn[r - (1 << k) + 1][k]);
}
signed main(){
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
n = read();
for(int i = 1; i <= n; ++i)
a[i] = mn[i][0] = read(), pre[i] = nxt[i] = i;
for(int i = 2; i <= n; ++i)
if(a[i - 1] < a[i]) pre[i] = pre[i - 1];
for(int i = n - 1; i >= 1; --i)
if(a[i + 1] < a[i]) nxt[i] = nxt[i + 1];
for(int j = 1; j <= 19; ++j)
for(int i = 1; i + (1 << j) - 1 <= n; ++i)
mn[i][j] = min(mn[i][j - 1], mn[i + (1 << (j - 1))][j - 1]);
q = read();
// for(int i = 44; i <= 58; ++i) cout << a[i] << " ";
// cout << endl;
while(q--){
int r1 = read(), c1 = read(), r2 = read(), c2 = read(), cw = c1, ct = c1;
// if(abs(r1 - r2) <= 10) cout << "???" << endl;
int ans = 1e9;
if(r1 < r2){
c1 = min(c1, qry(r1, r2));
// for(int i = r1; i <= r2; ++i) c1 = min(c1, a[i]);
ans = r2 - r1 + min({abs(c1 - c2), c2 + (c1 != 0), a[r2] - c2 + (c1 != a[r2])});
for(int i = r1; i <= r2; ++i){
ct = min(ct, a[i]);
int flag = (ct != a[i]);
c1 = a[i];
c1 = min(c1, qry(i + 1, r2));
// for(int j = i + 1; j <= r2; ++j) c1 = min(c1, a[j]);
ans = min(ans, r2 - r1 + flag + abs(c1 - c2));
}
for(int i = r2 + 1; i <= n; ++i){
ct = min(ct, a[i]);
int flag = (ct != a[i]);
c1 = a[i];
c1 = min(c1, qry(r2, i - 1));
// for(int j = i - 1; j >= r2; --j) c1 = min(c1, a[j]);
ans = min(ans, i - r1 + i - r2 + abs(c1 - c2) + flag);
}
ct = cw;
for(int i = r1 - 1; i >= 1; --i){
ct = min(ct, a[i]);
int flag = (ct != a[i]);
c1 = a[i];
c1 = min(c1, qry(i + 1, r2));
// for(int j = i + 1; j <= r2; ++j) c1 = min(c1, a[j]);
ans = min(ans, r2 - i + r1 - i + abs(c1 - c2) + flag);
}
write(ans), puts("");
}else{
c1 = min(c1, qry(r2, r1));
// for(int i = r2; i <= r1; ++i) c1 = min(c1, a[i]);
ans = r1 - r2 + min({abs(c1 - c2), c2 + (c1 != 0), a[r2] - c2 + (c1 != a[r2])});
c1 = cw;
for(int i = r1; i >= r2; --i){
ct = min(ct, a[i]);
int flag = (ct != a[i]);
c1 = a[i];
c1 = min(c1, qry(r2, i - 1));
// for(int j = i - 1; j >= r2; --j) c1 = min(c1, a[j]);
ans = min(ans, r1 - r2 + flag + abs(c1 - c2));
}
for(int i = r2 - 1; i >= 1; --i){
ct = min(ct, a[i]);
int flag = (ct != a[i]);
c1 = a[i];
c1 = min(c1, qry(i + 1, r2));
// for(int j = i + 1; j <= r2; ++j) c1 = min(c1, a[j]);
ans = min(ans, r1 - i + r2 - i + abs(c1 - c2) + flag);
}
ct = cw;
for(int i = r1 + 1; i <= n; ++i){
ct = min(ct, a[i]);
int flag = (ct != a[i]);
c1 = a[i];
c1 = min(c1, qry(r2, i - 1));
// for(int j = i - 1; j >= r2; --j) c1 = min(c1, a[j]);
ans = min(ans, i - r1 + i - r2 + abs(c1 - c2) + flag);
}
write(ans), puts("");
}
}
return 0;
}
# B. 子集
枚举中位数,二分选数个数,手动计算价值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
using namespace std;
namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
template <typename T> inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const int N = 2e5 + 10;
int n;
int a[N], s[N];
inline double calc(int x, int mid){
int sum = s[x] - s[x - mid - 1] + s[n] - s[n - mid];
return 1.0 * sum / (mid + mid + 1) - a[x];
}
signed main(){
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
n = read();
for(int i = 1; i <= n; ++i) a[i] = read();
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; ++i) s[i] = s[i - 1] + a[i];
double ans = 0;
if(n <= 2000){
for(int i = 1; i <= n; ++i){
int l = i - 1, r = n, sum = a[i], cnt = 1;
while(l >= 1 && r > i){
sum += a[l] + a[r];
l--, r--, cnt += 2;
ans = max(ans, 1.0 * sum / cnt - a[i]);
}
}
}else{
for(int i = 1; i <= n; ++i){
int l = 0, r = min(i - 1, n - i);
while(l <= r){
int mid = (l + r) >> 1;
// cout << l << " " << r << " " << midl << " " << midr << endl;
if(calc(i, mid) > calc(i, mid - 1)) l = mid + 1;
else r = mid - 1;
}
ans = max(ans, calc(i, r));
// ans = max({ans, calc(i, l), calc(i, r)});
}
}
printf("%.5lf\n", ans);
return 0;
}
# C. 子串
先建出 AC 自动机,设 表示到达 AC 自动机上 点时期望再加上多少个点结束。
那么有 。
设 是 AC 自动机上 的子节点,有转移:
根据这个式子建出方程组,然后高斯消元即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
using namespace std;
namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
template <typename T> inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const int N = 200;
const int mod = 1e9 + 7;
int T, n, inv26;
char s[N];
inline int add(int x) {return x >= mod ? x - mod : x;}
inline int sub(int x) {return x < 0 ? x + mod : x;}
inline int mul(int x, int y) {return 1ll * x * y % mod;}
inline int qpow(int a, int b){
int res = 1;
while(b){
if(b & 1) res = mul(res, a);
a = mul(a, a), b >>= 1;
}
return res;
}
int ch[N][26], tot, End[N];
int fail[N];
inline void insert(char *s){
int len = strlen(s), now = 0;
for(int i = 0; i < len; ++i){
int c = s[i] - 'a';
if(!ch[now][c]) ch[now][c] = ++tot;
now = ch[now][c];
}
End[now] = 1;
}
inline void build(){
queue <int> q;
for(int i = 0; i < 26; ++i)
if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty()){
int x = q.front(); q.pop();
for(int i = 0; i < 26; ++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];
}
}
}
int a[N][N];
inline void Gauss(int n){
for(int i = 0; i <= n; ++i){
for(int j = i + 1; j <= n && a[i][i] == 0; ++j)
if(a[j][i]) swap(a[i], a[j]);
int inv = qpow(a[i][i], mod - 2);
for(int j = i + 1; j <= n; ++j)
for(int k = n + 1; k >= i; --k)
a[j][k] = sub(a[j][k] - mul(a[i][k], mul(a[j][i], inv)));
}
for(int i = n; i >= 0; --i){
for(int j = i + 1; j <= n; ++j)
a[i][n + 1] = sub(a[i][n + 1] - mul(a[i][j], a[j][n + 1]));
a[i][n + 1] = mul(a[i][n + 1], qpow(a[i][i], mod - 2));
}
}
inline void solve(){
tot = 0;
memset(a, 0, sizeof(a));
memset(ch, 0, sizeof(ch));
memset(End, 0, sizeof(End));
memset(fail, 0, sizeof(fail));
n = read();
for(int i = 1; i <= n; ++i)
scanf("%s", s), insert(s);
build();
for(int i = 0; i <= tot; ++i){
a[i][i] = 1;
if(End[i]) continue;
a[i][tot + 1] = 1;
for(int j = 0; j < 26; ++j)
a[i][ch[i][j]] = sub(a[i][ch[i][j]] - inv26);
}
Gauss(tot);
write(a[0][tot + 1]), puts("");
}
signed main(){
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
T = read(), inv26 = qpow(26, mod - 2);
while(T--) solve();
return 0;
}