Solutions - 集训第二轮杂题选讲
D - Qoj9986 Shiori
一个比较好的势能分析题。
对于 op = 1 我们直接赋值。对于 op = 2 我们暴力枚举每个值,如果不存在该值那么这个东西就是 \(\text{mex}\),加上去;否则,将所有该值的极大同色节点弄出来加上 \(\text{mex}\)。对于 op = 3,解法显然。
我们考虑只有 op = 2 的情况,发现只有 \(a_i \le n\) 的节点有可能被更新。又发现每次有效更新中 \(a_i \gets a_i+k, k > a_i\),即 \(a_i\) 至少是之前的两倍,也就是说一个节点最多只能被有效更新 \(\log n\) 次。又发现 op = 1 最多会带来 \(q \log n\) 个新的极大同色节点,线段树操作为 \(\log\),那么复杂度为 \(\mathrm O(n \log n + q \log^2 n)\)。
#include <bits/stdc++.h>
#define llong long long
#define N 500005
using namespace std;
#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
x = 0; int w = 1;
char ch = gc();
while(ch < '0' || ch > '9'){
if(ch == '-') w = -w;
ch = gc();
}
while(ch >= '0' && ch <= '9')
x = (x<<3)+(x<<1)+(ch^48), ch = gc();
x *= w;
}
template<typename T, typename... Args>
inline void read(T& x, Args& ...y){
return read(x), read(y...);
}
constexpr llong inf = 1e9+7;
int n, q;
llong a[N];
struct Tag{
llong t1, t2;
Tag(){t1 = -1, t2 = 0;}
Tag(llong tg1, llong tg2){t1 = tg1, t2 = tg2;}
Tag& operator+=(Tag o){
if(o.t1 != -1){
t1 = o.t1, t2 = 0;
}
if(o.t2) t2 += o.t2;
return *this;
}
Tag& operator+=(int o){
t2 += o;
return *this;
}
Tag& operator=(int o){
t1 = o, t2 = 0;
return *this;
}
};
Tag tag[N<<2];
struct Node{
int l, r;
llong minn, maxn, sum;
Node operator+(Node o){
return {l, o.r, min(minn, o.minn), max(maxn, o.maxn), sum+o.sum};
}
Node& operator+=(Tag o){
if(o.t1 != -1){
minn = maxn = o.t1;
sum = o.t1*(r-l+1);
}
if(o.t2){
minn += o.t2, maxn += o.t2;
sum += o.t2*(r-l+1);
}
return *this;
}
Node& operator+=(int o){
minn += o, maxn += o;
sum += 1ll*o*(r-l+1);
return *this;
}
Node& operator=(int o){
minn = maxn = o;
sum = 1ll*o*(r-l+1);
return *this;
}
};
Node val[N<<2];
vector<pair<int, int>> his;
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define mid ((l+r)>>1)
inline void build(int x = 1, int l = 1, int r = n){
if(l == r) return val[x] = {l, l, a[l], a[l], a[l]}, void();
build(ls(x), l, mid), build(rs(x), mid+1, r);
val[x] = val[ls(x)]+val[rs(x)];
return;
}
inline void pushdown(int x){
val[ls(x)] += tag[x], tag[ls(x)] += tag[x];
val[rs(x)] += tag[x], tag[rs(x)] += tag[x];
tag[x] = Tag();
}
inline void assign(int L, int R, int k, int x = 1){
int l = val[x].l, r = val[x].r;
if(L <= l && R >= r) return val[x] = k, tag[x] = k, void();
pushdown(x);
if(L <= mid) assign(L, R, k, ls(x));
if(R > mid) assign(L, R, k, rs(x));
val[x] = val[ls(x)]+val[rs(x)];
return;
}
inline void add(int L, int R, int k, int x = 1){
int l = val[x].l, r = val[x].r;
if(L <= l && R >= r) return val[x] += k, tag[x] += k, void();
pushdown(x);
if(L <= mid) add(L, R, k, ls(x));
if(R > mid) add(L, R, k, rs(x));
val[x] = val[ls(x)]+val[rs(x)];
return;
}
inline void makemex(int L, int R, int k, int x = 1){
if(val[x].minn > k) return;
int l = val[x].l, r = val[x].r;
if(L <= l && R >= r && val[x].maxn == k){
his.emplace_back(l, r);
val[x] += inf, tag[x] += inf;
return;
}
pushdown(x);
if(L <= mid) makemex(L, R, k, ls(x));
if(R > mid) makemex(L, R, k, rs(x));
val[x] = val[ls(x)]+val[rs(x)];
return;
}
inline llong getmin(int L, int R, int x = 1){
int l = val[x].l, r = val[x].r;
if(L <= l && R >= r) return val[x].minn;
pushdown(x);
if(R <= mid) return getmin(L, R, ls(x));
if(L > mid) return getmin(L, R, rs(x));
return min(getmin(L, R, ls(x)), getmin(L, R, rs(x)));
}
inline llong getsum(int L, int R, int x = 1){
int l = val[x].l, r = val[x].r;
if(L <= l && R >= r) return val[x].sum;
pushdown(x);
if(R <= mid) return getsum(L, R, ls(x));
if(L > mid) return getsum(L, R, rs(x));
return getsum(L, R, ls(x))+getsum(L, R, rs(x));
}
int main(){
read(n, q);
for(int i = 1; i <= n; ++i) read(a[i]);
build();
while(q--){
int op, l, r, k;
read(op, l, r);
if(op == 1){
read(k);
assign(l, r, k);
}
if(op == 2){
int mex = 0;
while(getmin(l, r) == mex){
makemex(l, r, mex);
++mex;
}
for(auto now : his){
int ll = now.first, rr = now.second;
add(ll, rr, -inf);
}
add(l, r, mex);
his.clear();
}
if(op == 3)
printf("%lld\n", getsum(l, r));
}
return 0;
}
E - P4770 [NOI2018] 你的名字
我觉得我没有完全理解这个题,云里雾里就过了。
我们先考虑 \(l = 1, r = n\) 的情况。我们对 S 和 T 建出 SAM,考虑维护 T 的每个状态对应的节点在 S 上匹配的最长长度 \(l_i\),那么对于这个节点就有 \(len_i - \max\{l_i, len_{fa_i}\}\) 个串没有贡献。我们维护一个在 S 上的指针 \(x\) 和 T 上的指针 \(y\) 和一个当前匹配长度 \(cnt\),对于 T 上的一个字符 \(c\),将 \(y\) 跳一下,如果 \(x\) 有 \(c\) 的转移就 \(cur \gets cur+1, l_y \gets \max\{l_y, cur \}\),如果 \(x\) 没有 \(c\) 的转移就一直跳 fail 直到有转移并且 \(cur \gets len_{fa_u}\),然后 \(cur \gets cur+1, l_y \gets \max\{l_y, cur \}\)。最后 dfs 一下 \(y\) 的 parent 树,将 \(l_y\) 取到所有儿子的最大值,与 \(len_y\) 取 \(\min\) 即可。
然后考虑 \(l, r\) 任意的情况。我们不太能对任一区间建出 SAM,使用回滚莫队也是不现实的因为 SAM 不支持双端加字符。于是我们考虑对整个 S 建出 SAM 进行匹配。考虑使用线段树合并求 \(\text{endpos}\),\(y\) 还是在 T 上正常跳,然后对于 \(x\) 试图得到合法且最长的 \(cur\) 进行转移即可。
复杂度 \(\mathrm O(n \log n)\)。
#include <bits/stdc++.h>
#define llong long long
#define N 1000006
using namespace std;
#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
x = 0;
char ch = gc();
while(ch < '0' || ch > '9') ch = gc();
while(ch >= '0' && ch <= '9')
x = (x<<3)+(x<<1)+(ch^48), ch = gc();
}
inline void read(char* x){
*++x = gc();
while(*x == ' ' || *x == '\r' || *x == '\n') *x = gc();
while(*x != ' ' && *x != '\r' && *x != '\n') *++x = gc();
*x = '\0';
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
return read(x), read(y...);
}
int n, q;
char a[N], b[N];
struct SAM{
int l;
int fa[N<<1], len[N<<1], nxt[N<<1][32], tsiz;
int tag[N<<1];
SAM(){
tsiz = 1;
return;
}
inline void clear(){
for(int i = 1; i <= tsiz; ++i) fa[i] = len[i] = 0;
for(int i = 1; i <= tsiz; ++i)
for(int j = 1; j <= 26; ++j) nxt[i][j] = 0;
tsiz = 1;
return;
}
inline int insert(int c, int lst){
int x = lst, cur = ++tsiz;
len[cur] = len[lst]+1;
while(x && !nxt[x][c])
nxt[x][c] = cur, x = fa[x];
if(!x){
fa[cur] = 1;
return cur;
}
int y = nxt[x][c];
if(len[y] == len[x]+1){
fa[cur] = y;
}
else{
int z = ++tsiz;
len[z] = len[x]+1, fa[z] = fa[y];
for(int i = 1; i <= 26; ++i)
nxt[z][i] = nxt[y][i];
while(x && nxt[x][c] == y)
nxt[x][c] = z, x = fa[x];
fa[y] = fa[cur] = z;
}
return cur;
}
inline void insert(char* a, int n){
l = n;
int x = 1;
for(int i = 1; i <= l; ++i){
x = insert(a[i]^96, x);
tag[x] = i;
}
return;
}
};
SAM sam1, sam2;
int val[N<<6], ls[N<<6], rs[N<<6], ssiz, root[N];
vector<int> G[N<<1];
#define mid ((l+r)>>1)
inline int cpynode(int x){
int y = ++ssiz;
val[y] = val[x], ls[y] = ls[x], rs[y] = rs[x];
return y;
}
inline void modify(int pos, int &x, int l = 1, int r = sam1.l){
x = cpynode(x);
if(l == r) return val[x] = l, void();
if(pos <= mid) modify(pos, ls[x], l, mid );
else modify(pos, rs[x], mid+1, r);
val[x] = max(val[ls[x]], val[rs[x]]);
return;
}
inline int merge(int x, int y){
if(!x || !y) return x|y;
int z = cpynode(x);
ls[z] = merge(ls[x], ls[y]);
rs[z] = merge(rs[x], rs[y]);
val[z] = max(val[ls[z]], val[rs[z]]);
return z;
}
inline int query(int L, int R, int x, int l = 1, int r = sam1.l){
if(!x) return 0;
if(L <= l && R >= r) return val[x];
if(R <= mid) return query(L, R, ls[x], l, mid );
if(L > mid) return query(L, R, rs[x], mid+1, r);
return max(query(L, R, ls[x], l, mid), query(L, R, rs[x], mid+1, r));
}
#undef mid
inline void mergeup(int u){
for(int v : G[u])
mergeup(v), root[u] = merge(root[u], root[v]);
return;
}
inline void prework(){
for(int i = 2; i <= sam1.tsiz; ++i)
G[sam1.fa[i]].push_back(i);
for(int i = 1; i <= sam1.tsiz; ++i)
if(sam1.tag[i]) modify(sam1.tag[i], root[i]);
mergeup(1);
return;
}
int tmp[N<<1];
vector<int> G2[N<<1];
inline void dfs(int u){
for(int v : G2[u])
dfs(v), tmp[u] = max(tmp[u], tmp[v]);
tmp[u] = min(tmp[u], sam2.len[u]);
return;
}
int main(){
read(a), n = strlen(a+1);
sam1.insert(a, n), prework();
read(q);
while(q--){
int l, r, len;
read(b, l, r);
len = strlen(b+1);
sam2.clear(), sam2.insert(b, len);
for(int i = 1; i <= sam2.tsiz; ++i)
tmp[i] = 0, G2[i].clear();
int x = 1, y = 1, cnt = 0;
for(int i = 1; i <= len; ++i){
while(y && !sam2.nxt[y][b[i]^96]) y = sam2.fa[y];
if(y) y = sam2.nxt[y][b[i]^96];
else y = 1;
int nxtx = sam1.nxt[x][b[i]^96];
if(query(1, r, root[nxtx]) >= l+cnt){
++cnt;
x = nxtx;
tmp[y] = max(tmp[y], cnt);
continue;
}
while(x && (!nxtx || query(1, r, root[nxtx]) < l+sam1.len[sam1.fa[x]]))
x = sam1.fa[x], nxtx = sam1.nxt[x][b[i]^96];
if(!x){
x = 1;
cnt = 0;
}
else{
cnt = min(sam1.len[x]+1, query(1, r, root[nxtx])-l+1);
x = nxtx;
tmp[y] = max(tmp[y], cnt);
}
}
for(int i = 1; i <= sam2.tsiz; ++i)
G2[sam2.fa[i]].push_back(i);
dfs(1);
llong res = 0;
for(int i = 1; i <= sam2.tsiz; ++i)
res += sam2.len[i]-max(tmp[i], sam2.len[sam2.fa[i]]);
printf("%lld\n", res);
}
return 0;
}
F - P3674 小清新人渣的本愿
这题是因为太水了被开除 Ynoi 籍了吗。
考虑对于全局怎么做。做法是显然的,对于 op = 1,2 我们建出 \(a_i\) 和 \(-a_i+\Delta\) 的 bitset 然后询问的时候搞一下即可。对于 op = 3 我们直接枚举约数即可。
考虑区间查询,由于这是 lxl 的题,我们直接使用莫队。
复杂度 \(\mathrm(q \sqrt n + \frac{qn}{\omega})\)。
#include <bits/stdc++.h>
#define llong long long
#define N 100005
using namespace std;
#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
x = 0; int w = 1;
char ch = gc();
while(ch < '0' || ch > '9'){
if(ch == '-') w = -w;
ch = gc();
}
while(ch >= '0' && ch <= '9')
x = (x<<3)+(x<<1)+(ch^48), ch = gc();
x *= w;
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
return read(x), read(y...);
}
constexpr int B = 316, M = 1e5;
int n, q;
int a[N];
struct Query{
int op;
int l, r, x;
int bl, id;
bool operator<(const Query& o)const{
return bl<o.bl || (bl==o.bl && ((~bl&1)^(r<o.r)));
}
};
Query qry[N];
bool ans[N];
bitset<N> b1, b2;
int cnt[N];
#define add(x) (cnt[x]++ == 0 ? b1[x] = b2[M-x] = 1 : 0)
#define del(x) (--cnt[x] == 0 ? b1[x] = b2[M-x] = 0 : 1)
int main(){
read(n, q);
for(int i = 1; i <= n; ++i) read(a[i]);
for(int i = 1; i <= q; ++i){
read(qry[i].op, qry[i].l, qry[i].r, qry[i].x);
qry[i].bl = qry[i].l/B, qry[i].id = i;
}
sort(qry+1, qry+q+1);
int lp = 1, rp = 0;
for(int i = 1; i <= q; ++i){
int l = qry[i].l, r = qry[i].r;
while(lp > l) --lp, add(a[lp]);
while(rp < r) ++rp, add(a[rp]);
while(lp < l) del(a[lp]), ++lp;
while(rp > r) del(a[rp]), --rp;
int k = qry[i].x, id = qry[i].id;
if(qry[i].op == 1) ans[id] = (b1&(b1>>k)).any();
if(qry[i].op == 2) ans[id] = (b1&(b2>>(M-k))).any();
if(qry[i].op == 3){
int sq = ceil(sqrt(k));
for(int j = 1; j <= sq; ++j){
if(k % j || !cnt[j] || !cnt[k/j]) continue;
ans[id] = true;
break;
}
}
}
for(int i = 1; i <= q; ++i)
puts(ans[i] ? "hana" : "bi");
return 0;
}
G - Qoj7523 Partially Free Meal
决策单调性题,成功记忆恢复。
我们考虑对于单个 \(k\) 的解法。我们先按 \(b\) 排序,然后枚举每个 \(b\),我们找出所有 \(b\) 比 \(b_i\) 小的前 \(k\) 个 \(a_i\) 之和即可。
考虑每个 \(k\)。设 \(s(i, k)\) 为所有 \(b\) 比 \(b_i\) 小的前 \(k\) 个 \(a_i\) 之和,发现使用主席树可以 \(\mathrm O(n \log n) - \mathrm O(\log n)\) 求出 \(s\)。又发现对于一个位置 \(i\),\([1, i+1]\) 的第 \(k\) 大数不大于 \([1, i]\) 的第 \(k\) 大数,即 \(s(i, k)-s(i, k-1) \ge s(i+1, k)-s(i+1, k-1)\),即 \(s(i, k-1)+s(i+1, k) \le s(i, k)+s(i+1, k-1)\),即 \(s\) 满足四边形不等式。设 \(c(i, k) = \sum_{i=1}^k a_i + b_i\),发现 \(c(i, k-1)+c(i+1, k) = s(i, k-1)+s(i+1, k)+b_i+b_{i+1} \le s(i, k)+s(i+1, k-1)+b_i+b_{i+1} = c(i, k)+c(i+1, k-1)\),即 \(c\) 满足四边形不等式,于是 \(\mathrm{opt}\ c\) 单调。
于是整体二分即可。\(\mathrm O(n \log^2 n)\)。
#include <bits/stdc++.h>
#define llong long long
#define N 200005
using namespace std;
#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
x = 0; int w = 1;
char ch = gc();
while(ch < '0' || ch > '9'){
if(ch == '-') w = -w;
ch = gc();
}
while(ch >= '0' && ch <= '9')
x = (x<<3)+(x<<1)+(ch^48), ch = gc();
x *= w;
}
template<typename T, typename ...Args>
inline void read(T& x, Args& ...y){
return read(x), read(y...);
}
int n;
struct Item{
llong a, b;
};
Item a[N];
llong tmp[N], cnt;
llong ans[N];
struct Node{
llong val, siz;
inline Node operator+(Node o){
return {val+o.val, siz+o.siz};
}
inline Node& operator+=(int o){
val += tmp[o], ++siz;
return *this;
}
};
Node T[N<<5];
int ls[N<<5], rs[N<<5], root[N], tsiz;
#define mid ((l+r)>>1)
inline int cpynode(int x){
int y = ++tsiz;
T[y] = T[x], ls[y] = ls[x], rs[y] = rs[x];
return y;
}
inline void modify(int pos, int& x, int l = 1, int r = cnt){
x = cpynode(x);
if(l == r) return T[x] += pos, void();
if(pos <= mid) modify(pos, ls[x], l, mid );
else modify(pos, rs[x], mid+1, r);
T[x] = T[ls[x]]+T[rs[x]];
return;
}
inline llong query(int k, int x, int l = 1, int r = cnt){
if(l == r) return k*tmp[l];
if(T[ls[x]].siz >= k) return query(k, ls[x], l, mid);
else return T[ls[x]].val+query(k-T[ls[x]].siz, rs[x], mid+1, r);
}
#undef mid
inline void solve(int L, int R, int l, int r){
if(L > R) return;
int mid = (L+R)>>1, k = -1;
ans[mid] = (llong)1e18+3;
// if(mid % 1000 == 0) cerr << mid << endl;
for(int i = max(l, mid); i <= r; ++i){
llong res = query(mid, root[i])+a[i].b;
if(res < ans[mid]) ans[mid] = res, k = i;
}
assert(k != -1);
solve(L, mid-1, l, k);
solve(mid+1, R, k, r);
return;
}
int main(){
read(n);
for(int i = 1; i <= n; ++i) read(a[i].a, a[i].b);
sort(a+1, a+n+1, [&](Item o1, Item o2){return o1.b<o2.b;});
for(int i = 1; i <= n; ++i) tmp[++cnt] = a[i].a;
sort(tmp+1, tmp+cnt+1), cnt = unique(tmp+1, tmp+cnt+1)-tmp-1;
for(int i = 1; i <= n; ++i)
a[i].a = lower_bound(tmp+1, tmp+cnt+1, a[i].a)-tmp;
for(int i = 1; i <= n; ++i){
root[i] = root[i-1];
modify(a[i].a, root[i]);
}
solve(1, n, 1, n);
for(int i = 1; i <= n; ++i)
printf("%lld\n", ans[i]);
return 0;
}
K - P10144 [WC2024] 水镜
很好的题目,很有意境的标题。做法三千,只取一种写。
由于题目中只出现了 \(2L\),我们以 \(L\) 指代 \(2L\)。
我们维护对于两相邻点,合理的 \(L\) 的区间,然后使用 SegT 做这个东西即可。好像没了。
\(\mathrm O(n \log n)\)。
#include <bits/stdc++.h>
#define llong long long
#define N 500005
using namespace std;
#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
x = 0; int w = 1;
char ch = gc();
while(ch < '0' || ch > '9'){
if(ch == '-') w = -w;
ch = gc();
}
while(ch >= '0' && ch <= '9')
x = (x<<3)+(x<<1)+(ch^48), ch = gc();
x *= w;
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
return read(x), read(y...);
}
constexpr llong inf = (llong)1e18+3;
int n;
llong a[N];
llong ans;
struct Node{
int l, r;
llong L[2][2], R[2][2];
Node(){
L[0][0] = L[1][1] = R[0][1] = R[1][0] = -inf;
R[0][0] = R[1][1] = L[0][1] = L[1][0] = inf;
}
Node(int pos){
l = r = pos;
L[0][0] = L[1][1] = R[0][1] = R[1][0] = -inf;
R[0][0] = R[1][1] = L[0][1] = L[1][0] = inf;
}
Node operator+(Node o){
Node res;
// 0 0
res.l = l, res.r = o.r;
res.L[0][0] = res.L[0][1] = res.L[1][0] = res.L[1][1] = inf;
res.R[0][0] = res.R[0][1] = res.R[1][0] = res.R[1][1] = -inf;
if(a[r] < a[o.l]){
res.L[0][0] = min(res.L[0][0], max(L[0][0], o.L[0][0]));
res.L[0][1] = min(res.L[0][1], max(L[0][0], o.L[0][1]));
res.L[1][0] = min(res.L[1][0], max(L[1][0], o.L[0][0]));
res.L[1][1] = min(res.L[1][1], max(L[1][0], o.L[0][1]));
res.R[0][0] = max(res.R[0][0], min(R[0][0], o.R[0][0]));
res.R[0][1] = max(res.R[0][1], min(R[0][0], o.R[0][1]));
res.R[1][0] = max(res.R[1][0], min(R[1][0], o.R[0][0]));
res.R[1][1] = max(res.R[1][1], min(R[1][0], o.R[0][1]));
}
// 0 1
if(a[r] > a[o.l]){
res.L[0][0] = min(res.L[0][0], max(L[0][1], o.L[1][0]));
res.L[0][1] = min(res.L[0][1], max(L[0][1], o.L[1][1]));
res.L[1][0] = min(res.L[1][0], max(L[1][1], o.L[1][0]));
res.L[1][1] = min(res.L[1][1], max(L[1][1], o.L[1][1]));
res.R[0][0] = max(res.R[0][0], min(R[0][1], o.R[1][0]));
res.R[0][1] = max(res.R[0][1], min(R[0][1], o.R[1][1]));
res.R[1][0] = max(res.R[1][0], min(R[1][1], o.R[1][0]));
res.R[1][1] = max(res.R[1][1], min(R[1][1], o.R[1][1]));
}
// 0 1
res.L[0][0] = min(res.L[0][0], max({L[0][0], o.L[1][0], a[r]+a[o.l]}));
res.L[0][1] = min(res.L[0][1], max({L[0][0], o.L[1][1], a[r]+a[o.l]}));
res.L[1][0] = min(res.L[1][0], max({L[1][0], o.L[1][0], a[r]+a[o.l]}));
res.L[1][1] = min(res.L[1][1], max({L[1][0], o.L[1][1], a[r]+a[o.l]}));
res.R[0][0] = max(res.R[0][0], min(R[0][0], o.R[1][0]));
res.R[0][1] = max(res.R[0][1], min(R[0][0], o.R[1][1]));
res.R[1][0] = max(res.R[1][0], min(R[1][0], o.R[1][0]));
res.R[1][1] = max(res.R[1][1], min(R[1][0], o.R[1][1]));
// 1 0
res.L[0][0] = min(res.L[0][0], max(L[0][1], o.L[0][0]));
res.L[0][1] = min(res.L[0][1], max(L[0][1], o.L[0][1]));
res.L[1][0] = min(res.L[1][0], max(L[1][1], o.L[0][0]));
res.L[1][1] = min(res.L[1][1], max(L[1][1], o.L[0][1]));
res.R[0][0] = max(res.R[0][0], min({R[0][1], o.R[0][0], a[r]+a[o.l]}));
res.R[0][1] = max(res.R[0][1], min({R[0][1], o.R[0][1], a[r]+a[o.l]}));
res.R[1][0] = max(res.R[1][0], min({R[1][1], o.R[0][0], a[r]+a[o.l]}));
res.R[1][1] = max(res.R[1][1], min({R[1][1], o.R[0][1], a[r]+a[o.l]}));
return res;
}
llong getL(){
return min({L[0][0], L[0][1], L[1][0], L[1][1]});
}
llong getR(){
return max({R[0][0], R[0][1], R[1][0], R[1][1]});
}
};
Node val[N<<2];
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define mid ((l+r)>>1)
inline void build(int x = 1, int l = 1, int r = n){
if(l == r) return val[x] = Node(l), void();
build(ls(x), l, mid), build(rs(x), mid+1, r);
val[x] = val[ls(x)]+val[rs(x)];
return;
}
inline Node query(int L, int R, int x = 1, int l = 1, int r = n){
if(L <= l && R >= r) return val[x];
if(R <= mid) return query(L, R, ls(x), l, mid );
if(L > mid) return query(L, R, rs(x), mid+1, r);
return query(L, R, ls(x), l, mid)+query(L, R, rs(x), mid+1, r);
}
int main(){
read(n);
for(int i = 1; i <= n; ++i) read(a[i]);
build();
for(int i = 1, j = 1; i <= n; ++i){
while(j < n){
Node res = query(i, j+1);
if(res.getL() >= res.getR()) break;
++j;
}
ans += j-i;
}
printf("%lld", ans);
return 0;
}
L - Qoj10288 Now or Never
题目名称暗示做法这一块。
考虑一个贪心做法。从前往后枚举位。如果我们能把当前位及以后的位都消成 \(0\) 就消,否则如果能将当前位变成 \(1\) 就变。
用线性基维护,\(\mathrm O(\frac{nm^2 + qm^2}{\omega})\)。
#include <bits/stdc++.h>
#define llong long long
#define N 2005
using namespace std;
#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
x = 0; int w = 1;
char ch = gc();
while(ch < '0' || ch > '9') ch = gc();
while(ch >= '0' && ch <= '9')
x = (x<<3)+(x<<1)+(ch^48), ch = gc();
x *= w;
}
inline void read(char* x){
*x = gc();
while(*x == ' ' || *x == '\r' || *x == '\n') *x = gc();
while(*x != ' ' && *x != '\r' && *x != '\n') *++x = gc();
*x = '\0';
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
return read(x), read(y...);
}
int n, m, q;
bitset<N> b[N], sum[N], msk[N];
int vis[N];
bitset<N> tmp;
char s[N];
int main(){
read(n, m, q);
for(int i = 1; i <= n; ++i){
read(s), tmp = 0;
for(int j = 0; j < m; ++j)
if(s[j] == '1') tmp[j] = 1;
for(int j = 0; j < m && tmp.any(); ++j){
if(tmp[j]){
if(vis[j]) tmp ^= b[j];
else{
b[j] = tmp;
vis[j] = true;
break;
}
}
}
}
bitset<N> one = 1;
for(int i = m-1; ~i; --i)
msk[i] = msk[i+1]|(one<<i);
for(int i = m-1; ~i; --i){
if(!vis[i]) continue;
for(int j = i-1; ~j; --j)
if(vis[j] && b[j][i]) b[j] ^= b[i];
}
while(q--){
read(s), tmp = 0;
for(int j = 0; j < m; ++j)
if(s[j] == '1') tmp[j] = 1;
for(int j = m-1; ~j; --j){
sum[j] = sum[j+1];
if(vis[j] && tmp[j]) sum[j] ^= b[j];
}
for(int j = 0; j < m; ++j){
if((tmp&msk[j]) == sum[j]){
tmp ^= sum[j];
break;
}
else if(!tmp[j] && vis[j]) tmp ^= b[j];
}
for(int j = 0; j < m; ++j)
putchar_unlocked(tmp[j]^48);
putchar_unlocked('\n');
}
return 0;
}
Fun Fact:Hootime 寻找此题 AC 代码时发现找不到这道题的代码,当时 Hootime 以为自己罹患了妄想症。然后 Hootime 发现自己存在了 Luogu 题号的对应文件里。
M - Qoj5414 Stop, Yesterday Please No More
往日不再重现是吧。
这题是时,不想讲了。
#include <bits/stdc++.h>
#define llong long long
#define N 1003
using namespace std;
#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
x = 0; int w = 1;
char ch = gc();
while(ch < '0' || ch > '9'){
if(ch == '-') w = -w;
ch = gc();
}
while(ch >= '0' && ch <= '9')
x = (x<<3)+(x<<1)+(ch^48), ch = gc();
x *= w;
}
inline void read(char* x){
*++x = gc();
while(*x == ' ' || *x == '\r' || *x == '\n') *x = gc();
while(*x != ' ' && *x != '\r' && *x != '\n') *++x = gc();
*x = '\0';
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
return read(x), read(y...);
}
int n, m, k, l;
int minx, miny, maxx, maxy;
char a[N*N];
int mem1[N<<1][N<<1], mem2[N<<1][N<<1], mem3[N<<1][N<<1], mem4[N<<1][N<<1];
#define cnt(x,y) mem1[(x)+N][(y)+N]
#define pre1(x,y) mem2[(x)+N][(y)+N]
#define pre2(x,y) mem3[(x)+N][(y)+N]
#define res(x,y) mem4[(x)+N][(y)+N]
int _main(){
read(n, m, k);
read(a);
l = strlen(a+1);
int x = 0, y = 0;
minx = miny = maxx = maxy = 0;
cnt(0, 0) = 1;
for(int i = 1; i <= l; ++i){
if(a[i] == 'U') ++x;
if(a[i] == 'D') --x;
if(a[i] == 'L') ++y;
if(a[i] == 'R') --y;
minx = min(minx, x), maxx = max(maxx, x);
miny = min(miny, y), maxy = max(maxy, y);
if(x >= -n && x <= n && y >= -m && y <= m) cnt(x, y) |= 1;
}
for(int i = -n; i <= n; ++i){
for(int j = -m; j <= m; ++j){
pre1(i, j) = pre1(i, j-1)+cnt(i, j);
pre2(i, j) = pre2(i-1, j)+cnt(i, j);
}
}
int lx = 1+maxx, rx = n+minx;
int ly = 1+maxy, ry = m+miny;
int n2 = rx-lx, m2 = ry-ly;
int res = 0;
if(lx>rx || ly>ry){
printf("%d\n", n*m*(k==0));
goto clear;
}
res(0, 0) = 0;
for(int i = 0; i <= n2; ++i)
res(0, 0) += pre1(i, m2)-pre1(i, -1);
for(int i = 1; i <= n-lx; ++i)
res(i, 0) = res(i-1, 0)-(pre1(n2-i+1,m2)-pre1(n2-i+1,-1))+(pre1(-i,m2)-pre1(-i,-1));
for(int i = -1; i >= -(lx-1); --i)
res(i, 0) = res(i+1, 0)+(pre1(n2-i,m2)-pre1(n2-i,-1))-(pre1(-i-1,m2)-pre1(-i-1,-1));
for(int i = 1-lx; i <= n-lx; ++i){
for(int j = 1; j <= m-ly; ++j)
res(i, j) = res(i,j-1)-(pre2(n2-i,m2-j+1)-pre2(-i-1,m2-j+1))+(pre2(n2-i,-j)-pre2(-i-1,-j));
for(int j = -1; j >= -(ly-1); --j)
res(i, j) = res(i,j+1)+(pre2(n2-i,m2-j)-pre2(-i-1,m2-j))-(pre2(n2-i,-j-1)-pre2(-i-1,-j-1));
}
for(int i = 1-lx; i <= n-lx; ++i)
for(int j = 1-ly; j <= m-ly; ++j)
res += ((n2+1)*(m2+1)-res(i,j) == k);
printf("%d\n", res);
clear:;
for(int i = -n; i <= n; ++i)
for(int j = -m; j <= m; ++j)
cnt(i, j) = pre1(i, j) = pre2(i, j) = 0;
for(int i = 1-lx; i <= n-lx; ++i)
for(int j = 1-ly; j <= m-ly; ++j)
res(i, j) = 0;
return 0;
}
int T;
int main(){
read(T);
while(T--) _main();
return 0;
}
N - Qoj7745 Trapping Rain Water
思考题目性质,发现前缀 max 和后缀 max 构成一个连续段,于是我们用珂朵莉维护这个东西即可。
\(\mathrm O(n \log n)\)。注意细节。
#include <bits/stdc++.h>
#define llong long long
#define N 100005
using namespace std;
#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
x = 0; int w = 1;
char ch = gc();
while(ch < '0' || ch > '9'){
if(ch == '-') w = -w;
ch = gc();
}
while(ch >= '0' && ch <= '9')
x = (x<<3)+(x<<1)+(ch^48), ch = gc();
x *= w;
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
return read(x), read(y...);
}
int n, q;
llong a[N], maxn1[N], maxn2[N], pos;
llong s1, s2;
typedef tuple<int, int, llong> Node;
set<Node> odt1, odt2;
int _main(){
read(n);
for(int i = 1; i <= n; ++i){
read(a[i]), s1 += a[i];
if(a[i] > a[pos]) pos = i;
}
for(int i = 1; i <= n; ++i) maxn1[i] = max(maxn1[i-1], a[i]);
for(int i = n; i >= 1; --i) maxn2[i] = max(maxn2[i+1], a[i]);
for(int i = 1; i < pos; ++i) odt1.emplace(i, i, maxn1[i]), s2 += maxn1[i];
for(int i = pos; i <= n; ++i) odt2.emplace(i, i, maxn2[i]), s2 += maxn2[i];
read(q);
while(q--){
int x, k; read(x, k);
a[x] += k, s1 += k;
if(x < pos){
auto it1 = odt1.lower_bound({x+1, 0, 0}); --it1;
if(a[x] <= get<2>(*it1)) goto output;
int L = get<0>(*it1);
llong w = get<2>(*it1);
if(a[x] >= a[pos]){
for(auto it = it1, tmp = it1; it != odt1.end(); ){
s2 -= (get<1>(*it)-get<0>(*it)+1)*get<2>(*it);
tmp = it++;
odt1.erase(tmp);
}
if(L != x) odt1.emplace(L, x-1, w), s2 += ((x-1)-L+1)*w;
int R = get<1>(*odt2.begin());
odt2.erase(odt2.begin()); s2 -= (R-pos+1)*a[pos];
odt2.emplace(x, x, a[x]), s2 += a[x];
odt2.emplace(x+1, R, a[pos]), s2 += (R-(x+1)+1)*a[pos];
pos = x;
}
else{
auto it2 = it1;
while(it2 != odt1.end() && get<2>(*it2) <= a[x]) ++it2;
--it2;
int R = get<1>(*it2);
for(auto it = it1, tmp = it1; it != it2; ){
s2 -= (get<1>(*it)-get<0>(*it)+1)*get<2>(*it);
tmp = it++;
odt1.erase(tmp);
}
s2 -= (get<1>(*it2)-get<0>(*it2)+1)*get<2>(*it2);
odt1.erase(it2);
if(L != x) odt1.emplace(L, x-1, w), s2 += ((x-1)-L+1)*w;
odt1.emplace(x, R, a[x]), s2 += (R-x+1)*a[x];
}
}
else if(x > pos){
auto it1 = odt2.lower_bound({x+1, 0, 0}); --it1;
if(a[x] <= get<2>(*it1)) goto output;
int R = get<1>(*it1);
llong w = get<2>(*it1);
if(a[x] > a[pos]){
for(auto it = odt2.begin(), tmp = it; it != it1; ){
s2 -= (get<1>(*it)-get<0>(*it)+1)*get<2>(*it);
tmp = it++;
odt2.erase(tmp);
}
s2 -= (get<1>(*it1)-get<0>(*it1)+1)*get<2>(*it1);
odt2.erase(it1);
if(R != x) odt2.emplace(x+1, R, w), s2 += (R-(x+1)+1)*w;
odt1.emplace(pos, x-1, a[pos]), s2 += ((x-1)-pos+1)*a[pos];
odt2.emplace(x, x, a[x]), s2 += a[x];
pos = x;
}
else{
auto it2 = it1;
while(it2 != odt2.begin() && get<2>(*it2) <= a[x]) --it2;
int L;
if(get<2>(*it2) <= a[x]) L = get<0>(*it2);
else L = get<1>(*it2)+1, ++it2;
for(auto it = it2, tmp = it2; it != it1; ){
s2 -= (get<1>(*it)-get<0>(*it)+1)*get<2>(*it);
tmp = it++;
odt2.erase(tmp);
}
s2 -= (get<1>(*it1)-get<0>(*it1)+1)*get<2>(*it1);
odt2.erase(it1);
if(R != x) odt2.emplace(x+1, R, w), s2 += (R-(x+1)+1)*w;
odt2.emplace(L, x, a[x]), s2 += (x-L+1)*a[x];
}
}
else{
int R = get<1>(*odt2.begin());
llong w = get<2>(*odt2.begin());
auto it1 = odt2.begin();
s2 -= (get<1>(*it1)-get<0>(*it1)+1)*get<2>(*it1);
odt2.erase(it1);
if(R != x) odt2.emplace(x+1, R, w), s2 += (R-(x+1)+1)*w;
odt2.emplace(x, x, a[x]), s2 += a[x];
}
output: printf("%lld\n", s2-s1);
}
odt1.clear(), odt2.clear();
for(int i = 1; i <= n; ++i) maxn1[i] = maxn2[i] = 0;
s1 = s2 = pos = 0;
return 0;
}
int T;
int main(){
read(T);
while(T--) _main();
return 0;
}
O - Qoj9488 Do Not Turn Back
不要回头!(
特判 \(k = 1\)。
考虑回头的向量,发现是每一个节点的对应值乘上 \(deg_i - 1\)。于是构造转移矩阵 \(\begin{bmatrix} A & -C \\ I & 0 \end{bmatrix}\),其中 \(A\) 是邻接矩阵,\(C\) 是对角矩阵,其中 \(C_{i, i} = deg_i - 1\)。构造初始状态 \(\begin{bmatrix} f_1 \\ f_2 \end{bmatrix}\),注意不是 \(\begin{bmatrix} f_0 \\ f_1 \end{bmatrix}\)。然后矩阵快速幂跑一下即可。
时间复杂度 \(\mathrm O(n^3 \log k)\)。
#include <bits/stdc++.h>
#define llong long long
#define N 205
using namespace std;
#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
x = 0; int w = 1;
char ch = gc();
while(ch < '0' || ch > '9'){
if(ch == '-') w = -w;
ch = gc();
}
while(ch >= '0' && ch <= '9')
x = (x<<3)+(x<<1)+(ch^48), ch = gc();
x *= w;
}
template<typename T, typename ...Args>
inline void read(T& x, Args& ...y){
return read(x), read(y...);
}
constexpr llong p = 998244353;
int n, m, k;
llong a[N][N], tmp1[N][N];
llong b[N], tmp2[N], deg[N];
inline void op1(){
#ifdef DEBUG
cerr << "Before op1:" << endl;
for(int i = 1; i <= n*2; ++i) cerr << b[i] << " ";
cerr << endl << endl;
for(int i = 1; i <= n*2; ++i){
for(int j = 1; j <= n*2; ++j)
cerr << a[i][j] << " ";
cerr << endl;
}
cerr << endl;
#endif
for(int i = 1; i <= n*2; ++i) tmp2[i] = 0;
for(int i = 1; i <= n*2; ++i)
for(int j = 1; j <= n*2; ++j)
tmp2[i] = (tmp2[i]+a[i][j]*b[j])%p;
for(int i = 1; i <= n*2; ++i) b[i] = tmp2[i];
#ifdef DEBUG
cerr << "After op1:" << endl;
for(int i = 1; i <= n*2; ++i) cout << b[i] << " ";
cerr << endl << endl;
#endif
return;
}
inline void op2(){
#ifdef DEBUG
cerr << "Before op2:" << endl;
for(int i = 1; i <= n*2; ++i){
for(int j = 1; j <= n*2; ++j)
cerr << a[i][j] << " ";
cerr << endl;
}
cerr << endl;
#endif
for(int i = 1; i <= n*2; ++i)
for(int j = 1; j <= n*2; ++j) tmp1[i][j] = 0;
for(int i = 1; i <= n*2; ++i)
for(int j = 1; j <= n*2; ++j)
for(int k = 1; k <= n*2; ++k)
tmp1[i][j] = (tmp1[i][j]+a[i][k]*a[k][j])%p;
for(int i = 1; i <= n*2; ++i)
for(int j = 1; j <= n*2; ++j) a[i][j] = tmp1[i][j];
#ifdef DEBUG
cerr << "After op2:" << endl;
for(int i = 1; i <= n*2; ++i){
for(int j = 1; j <= n*2; ++j)
cerr << a[i][j] << " ";
cerr << endl;
}
cerr << endl;
#endif
return;
}
int main(){
read(n, m, k);
for(int i = 1; i <= m; ++i){
int u, v; read(u, v);
++a[u][v], ++a[v][u];
++deg[u], ++deg[v];
}
if(k == 1){
printf("%lld", a[1][n]);
return 0;
}
k -= 2;
for(int i = 1; i <= n; ++i)
a[i+n][i] = 1, a[i][i+n] = (p-deg[i]+1)%p;
for(int i = 1; i <= n; ++i) b[i+n] = a[1][i];
for(int i = 1; i <= n; ++i)
for(int j = 2; j <= n; ++j)
b[j] = (b[j]+a[j][i]*b[i+n])%p;
while(k){
if(k & 1) op1();
op2(), k >>= 1;
}
printf("%lld", b[n]);
return 0;
}

浙公网安备 33010602011771号