https://atcoder.jp/contests/abc234/tasks
G - Divide a Sequence 长n数组A
2^{n-1}种方式切割成非空顺序数字
f(切割) = $\prod$每个子数组的 (max-min)
求 $\sum$ f(所有切割)
mod 998244353
范围 2s 1024mb
我的思路 dp搞一搞
考虑最后一个数组长度 dp[i] = dp[i-len] * ((max-min)([i-len+1..i]))
但这样就O(n) 状态, n^2转移了
然后,算的时候,max和min可以拆开, 但是如果 两两不等的话, 即使用了 区间sum dp * max, 也可能max需要一个一个枚举,也是n^2
问题变成说
dp[i] = sum max(a[i-len+1..i]) * dp[i-len] - min(a[i-len+1..i]) * dp[i-len]
考虑相邻转移
i-1: sum max(a[j+1..i-1]) * dp[j]
a[i]是[j+1..i]的最大值
, 那么找最小的j
, 这之家你的贡献 = a[i] * sum dp[j..i-1]
a[i]不是[j+1..i]
的最大值, 那么这贡献 = sum max(a[j+1..i])*dp[j] = sum max(a[j+1..i-1])*dp[j]
因此相邻的dp虽然没有直接关系, 但是可以利用上次的计算
dp[i] = a[i] * sum(dp[j..i-1]) + sum f(0..j)
min同理
似乎就没了
代码 https://atcoder.jp/contests/abc234/submissions/34662212
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 #include <bits/stdc++.h> #include <atcoder/modint> using mint = atcoder::modint998244353;#define rep(i,a,n) for (int i=a;i<(int)n;i++) int read () {int r;scanf ("%d" ,&r);return r;}int a[300010 ];mint pdp[300010 ] = {1 }; int main () { int n = read (); rep (i,1 ,n+1 ) a[i] = read (); std::vector<std::pair<int ,mint> > maxi = {}; std::vector<std::pair<int ,mint> > mini = {}; rep (i,1 ,n+1 ){ while (maxi.size () && a[maxi.back ().first] <= a[i]) maxi.pop_back (); maxi.push_back ({i,(maxi.size ()?maxi.back ().second:0 ) + a[i] * (pdp[i-1 ] - (maxi.size ()?pdp[maxi.back ().first-1 ]:0 ))}); while (mini.size () && a[mini.back ().first] >= a[i]) mini.pop_back (); mini.push_back ({i,(mini.size ()?mini.back ().second:0 ) + a[i] * (pdp[i-1 ] - (mini.size ()?pdp[mini.back ().first-1 ]:0 ))}); pdp[i] = pdp[i-1 ] + (maxi.back ().second - mini.back ().second); } printf ("%d\n" ,(pdp[n] - pdp[n-1 ]).val ()); return 0 ; }
Ex - Enumerate Pairs 平面N个点, 整数k
输出所有几何距离<=k的点对, 按字典序
保证结果 <= 4e5
范围 n 2e5
k 1.5e9
xi,yi [0,1e9]
4s
1024mb
我的思路 最无脑就是n^2暴力, 除了会TLE就是对的
如果是长方形还好, 可以 二维线段树, 但是圆形并不会
剩下想法分块和二分?
题解 每个点分配到(xi/k,yi/k) 中
这样每个点在它的块 的8临块 中找可匹配点
暴力即可, 充分性显然
复杂度证明! O(N+M)
对于块有x个点, 显然其内部距离小于k的点对是 x^2级别的,(证明,4等分成4个正方形子块, 至少一个块的数量级是x,而其内部两两距离小于k)
因此 所有块的 x^2 和的级别 <= M
因此块间 x1x2 <= max(x1,x2)^2 <= M
得证
代码 https://atcoder.jp/contests/abc234/submissions/34662441
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 #include <bits/stdc++.h> using namespace std;typedef long long ll;#define rep(i,a,n) for (ll i=a;i<(ll)n;i++) ll read () {ll r;scanf ("%lld" ,&r);return r;}const ll MAXY = 2e9 ;int dx[] = {-1 ,-1 ,-1 , 0 , 0 , 0 , 1 , 1 , 1 };int dy[] = {-1 , 0 , 1 ,-1 , 0 , 1 ,-1 , 0 , 1 };ll enc (ll x,ll y) { return x*MAXY+y;} int main () { int n = read (); ll k = read (); vector<pair<ll,ll>> pts (n+1 ); map<ll,vector<int >> mp; rep (i,1 ,n+1 ){ ll x = read () + k; ll y = read () + k; pts[i] = {x,y}; mp[enc (x/k,y/k)].push_back (i); } vector<pair<int ,int >> res; rep (i,1 ,n+1 ){ auto [x,y] = pts[i]; rep (t,0 ,9 ) for (int j: mp[enc (x/k+dx[t],y/k+dy[t])]) if (i<j) { auto [xj,yj] = pts[j]; if ((x-xj)*(x-xj) + (y-yj)*(y-yj) <= k*k) res.push_back ({i,j}); } } sort (res.begin (),res.end ()); printf ("%d\n" ,(int )res.size ()); for (auto [i,j]: res) printf ("%d %d\n" ,i,j); return 0 ; }
总结 G
dp,前缀和,单调栈,没啥难的
Ex
分块, 神奇的分块, 不会证明,
参考 官方题解