[Ynoi 2013] 文化课题解
闲聊
哎呀这题都写加调了一天,还真不如回去学文化课。
Ynoi 日常出神题,但是这题不像 5062 4118 在科技树上压制你,想一想还是可做的,确实是道好题。
话说这是机房某佬模拟赛的 T4,补题的时候,出于对数据结构的爱就把它补了,然后写了一天,故有此记。
由于此题解的代码只是为了方便理解而没有测过,因此除了AC代码可能出错
正文
就是维护一个数字序列和一个符号序列,符号序列只有加和乘(0是加,1是乘)。例如,数字序列 \(1,2,3,4,5\) 和符号序列 \(0,1,0,1\) 代表的是 \(1+2\times 3+4\times 5\)。优先级是正常的优先级。询问是截出一段求他的值,支持数字序列和符号序列的推平,模 \(1000000007\)。\((1\le n,m\le 10^5)\)
不考虑修改,这个东西可以仿照区间最大子段和,把他拍到线段树上,发现只用维护区间右边的符号,结果,从右边开始的最长连乘段长度和结果,从左边开始的最长连乘段长度和结果,就可以 \(O(1)\) 合并
struct Node{int l, r, len, sm, lm, rm, ll, rl, rop; Node(){};};
//l,r为端点,len是长度,sm是答案,ll,lm为从左开始的连乘段长度和结果,rl,rm一样,rop是区间有段的符号
inline void phur(Node &r, Node &ls, Node &rs){
r.len = ls.len + rs.len; r.rop = rs.rop; r.l = ls.l, r.r = rs.r;
if(ls.rop)r.sm=((ls.sm+rs.sm-ls.rm-rs.lm+1ll*ls.rm*rs.lm)%mod+mod)%mod;
else r.sm = (ls.sm + rs.sm) % mod;
if(ls.rop && (ls.ll == ls.len)) r.lm = 1ll * ls.sm * rs.lm % mod, r.ll = ls.ll + rs.ll;
else r.lm = ls.lm, r.ll = ls.ll;
if(ls.rop && (rs.rl == rs.len)) r.rm = 1ll * rs.sm * ls.rm % mod, r.rl = rs.rl + ls.rl;
else r.rm = rs.rm, r.rl = rs.rl;
} // 思考一下为什么是这些
然后你就有了5分的好成绩。
现在来思考怎么修改符号,推平一个区间的答案会把他的答案变成区间和或者区间乘,同理会对左右连乘段有修改。所以再维护区间和积,区间最左边的值,区间最右边的值。可以看代码理解一下:
}struct node{
int l, r, len, rl, lm, rm, sm;
int rop, tgp, ad, ml, lv, rv;
}t[maxn << 2];
//ad是和,ml是积,lv rv是左右的值。tgp是修改符号的 tag
}inline void pdp(int p, int x){ t[p].rop = t[p].tgp = x;
if(x == 0) t[p].sm = t[p].ad, t[p].lm = t[p].lv, t[p].rm = t[p].rv, t[p].ll= t[p].rl = 1;
else t[p].sm = t[p].lm = t[p].rm = t[p].ml, t[p].ll = t[p].rl = t[p].len
}
然后套上一个线段树的板子,你就可以拿到24分
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10; const int mod = 1e9 + 7;
int n, m, a[maxn], b[maxn];
}struct node{
int l, r, len, rl, lm, rm, sm;
int rop, tgp, ad, ml, lv, rv;
}t[maxn << 2];
struct Node{int l, r, len, sm, lm, rm, ll, rl, rop; Node(){};};
inline void phun(node &r, node &ls, node &rs){
r.ad = (ls.ad + rs.ad) % mod, r.ml = 1ll * ls.ml * rs.ml % mod;
r.lv = ls.lv, r.rv = rs.rv, r.rop = rs.rop;
if(ls.rop)r.sm=((ls.sm+rs.sm-ls.rm-rs.lm+1ll*ls.rm*rs.lm)%mod+mod)%mod;
else r.sm = (ls.sm + rs.sm) % mod;
if(ls.rop && (ls.ll == ls.len)) r.lm = 1ll * ls.sm * rs.lm % mod, r.ll = ls.ll + rs.ll;
else r.lm = ls.lm, r.ll = ls.ll;
if(ls.rop && (rs.rl == rs.len)) r.rm = 1ll * rs.sm * ls.rm % mod, r.rl = rs.rl + ls.rl;
else r.rm = rs.rm, r.rl = rs.rl;
}inline void phur(Node &r, Node &ls, Node &rs){
r.len = ls.len + rs.len; r.rop = rs.rop; r.l = ls.l, r.r = rs.r;
if(ls.rop)r.sm=((ls.sm+rs.sm-ls.rm-rs.lm+1ll*ls.rm*rs.lm)%mod+mod)%mod;
else r.sm = (ls.sm + rs.sm) % mod;
if(ls.rop && (ls.ll == ls.len)) r.lm = 1ll * ls.sm * rs.lm % mod, r.ll = ls.ll + rs.ll;
else r.lm = ls.lm, r.ll = ls.ll;
if(ls.rop && (rs.rl == rs.len)) r.rm = 1ll * rs.sm * ls.rm % mod, r.rl = rs.rl + ls.rl;
else r.rm = rs.rm, r.rl = rs.rl;
}inline void ins(int p, int l, int r){
t[p].l = l; t[p].r = r; t[p].len = r - l + 1; t[p].tgv = t[p].tgp = -1;
t[p].ctl = sqrt(t[p].len), t[p].ct = new int[t[p].ctl + 1];
if(l == r){ t[p].ll = t[p].rl = 1; t[p].rop = b[l]; t[p].ad = t[p].ml =
t[p].rv = t[p].lv = t[p].lm = t[p].rm = t[p].sm = a[l]; t[p].ct[1] = 1;return;
}int mid = l + r >> 1; ins(p * 2, l, mid);
ins(p * 2 + 1, mid + 1, r); phun(t[p], t[p * 2], t[p * 2 + 1]);
}inline void pdp(int p, int x){
t[p].rop = t[p].tgp = x;
if(x == 0){
t[p].sm = t[p].ad; t[p].lm = t[p].lv, t[p].rm = t[p].rv;
for(int i = 1; i <= t[p].ctl; i++) t[p].ct[i] = 0; t[p].ll = t[p].rl = 1;
t[p].o.clear(); t[p].ct[1] = t[p].len; return;
}else{
t[p].sm = t[p].ml; t[p].lm = t[p].rm = t[p].ml;
t[p].ll = t[p].rl = t[p].len; t[p].o.clear();
for(int i = 1; i <= t[p].ctl; i++) t[p].ct[i] = 0;
if(t[p].len <= t[p].ctl) t[p].ct[t[p].len]++; else t[p].o.emplace_back(t[p].len);
}
}inline void phd(int p){
if(t[p].tgp + 1) pdp(p * 2, t[p].tgp), pdp(p * 2 + 1, t[p].tgp);
t[p].tgp = -1;
}inline void upd(int p, int l, int r, int op, int x){
if(t[p].l >= l && t[p].r <= r) {if(op == 1) pdv(p, x); else pdp(p, x); return;}
int mid = t[p].l + t[p].r >> 1; phd(p);
if(l <= mid) upd(p * 2, l, r, op, x); if(r > mid) upd(p * 2 + 1, l, r, op, x);
phun(t[p], t[p * 2], t[p * 2 + 1]);
}inline Node qry(int p, int l, int r){
if(t[p].l >= l && t[p].r <= r) return Node(t[p]);
phd(p); Node ro, ls, rs; int mid = t[p].l + t[p].r >> 1;
if(r <= mid) return qry(p * 2, l, r); if(l > mid) return qry(p * 2 + 1, l, r);
ls = qry(p * 2, l, r); rs = qry(p * 2 + 1, l, r); phur(ro, ls, rs); return ro;
}int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);std::cout.tie(0);
cin >> n >> m; long tp;
for(int i = 1; i <= n; i++) cin >> tp, a[i] = tp % mod;
for(int i = 1; i < n; i++) cin >> b[i]; ins(1, 1, n);
while(m--){ int op, l, r; long x; cin >> op >> l >> r;
//if(op == 1) cin >> x, upd(1, l, r, 1, x % mod);
if(op == 2) cin >> x, upd(1, l, r, 2, x);
if(op == 3) cout << qry(1, l, r).sm << '\n';
}
}
有的地方写的比较繁琐是因为从AC代码改的,然后那个AC代码写得是真比较不好评价。
现在来考虑修改数值,现在一个节点会改变的只有跟数值有关的:答案,左右连乘结果,和积,最左和左右的值。除了答案的所有都是很好维护的。维护答案需要知道所有连乘段的长度,这个对每个结点开一个vector 维护之,注意修改符号时也会修改这个,也要维护。
其实到这里都是好写和好想的。
struct node{
int l, r, len, ll, rl, lm, rm, lv, rv, sm;
int rop, tgp, tgv, ad, ml; vector <int> o; //o是那个vector
}t[maxn << 2];
inline void phun(node &r, node &ls, node &rs){
r.ad = (ls.ad + rs.ad) % mod, r.ml = 1ll * ls.ml * rs.ml % mod;
r.lv = ls.lv, r.rv = rs.rv, r.rop = rs.rop;
if(ls.rop)r.sm=((ls.sm+rs.sm-ls.rm-rs.lm+1ll*ls.rm*rs.lm)%mod+mod)%mod;
else r.sm = (ls.sm + rs.sm) % mod;
if(ls.rop && (ls.ll == ls.len)) r.lm = 1ll * ls.sm * rs.lm % mod, r.ll = ls.ll + rs.ll;
else r.lm = ls.lm, r.ll = ls.ll;
if(ls.rop && (rs.rl == rs.len)) r.rm = 1ll * rs.sm * ls.rm % mod, r.rl = rs.rl + ls.rl;
else r.rm = rs.rm, r.rl = rs.rl;
r.o.clear(); for(auto v:ls.o) r.o.emplace_back(v); for(auto v:rs.o) r.o.emplace_back(v);
if(ls.rop) r.o.erase(r.o.find(ls.rl)), r.o.erase(r.o.find(rs.ll)), r.o.emplace_back(ls.rr+rs.ll);
//思考为什么有上面这行
}inline void ins(int p, int l, int r);
inline void pdp(int p, int x){
//只放有修改的
if(x == 0) {t[p].o.clear(); for(int i = 1; i <= t[p].len; i++) t[p].o.emplace_back(1);}
else t[p].o.clear(), t[p].o.emplace_back(t[p].len);
}inline void pdv(int p, int x){ int tp = 1, ps = 0;
t[p].tgv = x; t[p].ad = 1ll * x * t[p].len % mod;
t[p].ml = qpow(x, t[p].len); t[p].lv = t[p].rv = x;
t[p].lm = qpow(x, t[p].ll); t[p].rm = qpow(x, t[p].rl); //以上都是刚才说好维护的
t[p].sm = 0; for(auto v:t[p].o) t[p].sm = (t[p].sm + qpow(x, v)) % mod;
}inline void upd(int p, int l, int r, int op, int x);//这个就不放了
记得pushdown的时候tgv也要修改。当然有更优秀的实现方式,但是既然过不了就这么写一下让大家理解。
现在的问题在于,如果区间有一堆长度非常小的连乘段,空间和时间都会寄寄。考虑怎么高效地维护这些信息,直接开数组空间是一样的,而且遍历一遍也不会让时间降下来。
接下来是这道题比较妙的一个点,也是一个经典的trick。既然和区间长度有关,那么长度小于 \(\sqrt n\) 的不超过 \(\sqrt n\) 种(废话),大于 \(\sqrt n\) 的不超过 \(\sqrt n\) 段。所以分治一下,长度小的对于每个区间 new 出一个桶来计算,长的区间一样使用 vector。
这样单次 pushup 的时间复杂度是 \(O(\sqrt {len}\log {len})\) 的,甚至还是难以接受。因为 \(\log{len}\) 来自于快速幂,这一项是可以优化的,让 vector 里存的长度非降,这样每次只用上一次的幂乘上上次和这次的次数差。
for(auto v:t[p].o) (t[p].sm += qpow(x, v)) %= mod;
变成
int ps = 0, ls = 1;
for(auto v:t[p].o) (t[p].sm += (ls *= qpow(x, ps-v)) %= mod) %= mod, ps = v;
这样就完美地变成 \(O(\sqrt{len}+\log{len})=O(\sqrt{len})\) 了。
然后由于要求 vector 内部元素是非降的,pushup时使用归并,复杂度不会上升。
然后经过一些我都不会的证明,这么做的整体复杂度是 \(O(n\sqrt{n})\)。作为数学小白这部分是真不会,可以参考这位大佬的博客。
然后你发现这个部分加上了之后代码突然变得十分甚至九分难写和恶心。
还是先把要修改的地方单独列一下吧
//以下为每个函数中新增的内容
struct node{
int l, r, len, ctl, ll, rl, lm, rm, lv, rv, sm;
int rop, tgp, tgv, ad, ml, *ct; vector <int> o;
}t[maxn << 2]; //ctl就是 sqrt(len),*ct是用来new出数组的
inline void phun(node &r, node &ls, node &rs){
for(int i = 1; i <= r.ctl; i++) r.ct[i] = 0; r.o.clear();
for(int i = 1; i <= sqrt(ls.len); i++) r.ct[i] += ls.ct[i];
for(int i = 1; i <= sqrt(rs.len); i++) r.ct[i] += rs.ct[i];
auto lbg = ls.o.begin(), rbg = rs.o.begin(), led = ls.o.end(), red = rs.o.end();
while(lbg != led && rbg != red){
if(*lbg < *rbg) {if(*lbg <= r.ctl) r.ct[*lbg]++; else r.o.emplace_back(*lbg); lbg++;}
else if(*lbg>*rbg){if(*rbg<=r.ctl) r.ct[*rbg]++; else r.o.emplace_back(*rbg); rbg++;}
else {if(*rbg<=r.ctl) r.ct[*rbg]+=2;else r.o.emplace_back(*rbg),
r.o.emplace_back(*lbg); rbg++; lbg++;}
}while(lbg != led) {if(*lbg <= r.ctl) r.ct[*lbg]++; else r.o.emplace_back(*lbg); lbg++;}
while(rbg != red) {if(*rbg <= r.ctl) r.ct[*rbg]++; else r.o.emplace_back(*rbg); rbg++;} //以上为归并
if(ls.rop){ int tl;
if(ls.rl <= r.ctl) r.ct[ls.rl] --; else r.o.erase(lower_bound(r.o.begin(), r.o.end(), ls.rl));
if(rs.ll <= r.ctl) r.ct[rs.ll] --; else r.o.erase(lower_bound(r.o.begin(), r.o.end(), rs.ll));
tl=ls.rl+rs.ll;if(tl<=r.ctl)r.ct[tl]++;else r.o.insert(lower_bound(r.o.begin(),r.o.end(),tl),tl);
} //由于要求有序,修改时要lower_bound加上insert
}inline void ins(int p, int l, int r){
t[p].ctl = sqrt(t[p].len), t[p].ct = new int[t[p].ctl + 1]; if(l == r) t[p].ct[1] ++;
}inline void pdp(int p, int x){
if(x == 0){
for(int i = 1; i <= t[p].ctl; i++) t[p].ct[i] = 0; t[p].ll = t[p].rl = 1;
t[p].o.clear(); t[p].ct[1] = t[p].len;
}else{
for(int i = 1; i <= t[p].ctl; i++) t[p].ct[i] = 0; t[p].o.clear();
if(t[p].len <= t[p].ctl) t[p].ct[t[p].len]++; else t[p].o.emplace_back(t[p].len);
}
}inline void pdv(int p, int x){ int tp = 1, ps = 0;
t[p].sm = 0; for(int i = 1; i <= t[p].ctl; i++){
if(!t[p].ct[i]) continue; if(ps != i) tp = 1ll * tp * qpow(x, i - ps) % mod, ps = i;
t[p].sm = (t[p].sm + 1ll * t[p].ct[i] * tp % mod) % mod;
} auto bg = t[p].o.begin(), ed = t[p].o.end();
while(bg != ed){
if(*bg != ps) tp = 1ll * tp * qpow(x, *bg - ps) % mod, ps = *bg;
t[p].sm = (t[p].sm + tp) % mod; bg++;}
}
最后 AC 代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10; const int mod = 1e9 + 7;
int n, m, a[maxn], b[maxn];
inline int qpow(int a, int b){
int rs = 1; while(b){
if(b & 1) rs = 1ll * rs * a % mod;
a = 1ll * a * a % mod; b >>= 1;
} return rs;
}struct node{
int l, r, len, ctl, ll, rl, lm, rm, lv, rv, sm;
int rop, tgp, tgv, ad, ml, *ct; vector <int> o;
}t[maxn << 2];
struct Node{int l, r, len, sm, lm, rm, ll, rl, rop; Node(){};
Node(node x){l=x.l,r=x.r,len=x.len,sm=x.sm,lm=x.lm,rm=x.rm,ll=x.ll,rl=x.rl,rop=x.rop;}};
inline void phun(node &r, node &ls, node &rs){
r.ad = (ls.ad + rs.ad) % mod, r.ml = 1ll * ls.ml * rs.ml % mod;
r.lv = ls.lv, r.rv = rs.rv, r.rop = rs.rop;
if(ls.rop)r.sm=((ls.sm+rs.sm-ls.rm-rs.lm+1ll*ls.rm*rs.lm)%mod+mod)%mod;
else r.sm = (ls.sm + rs.sm) % mod;
if(ls.rop && (ls.ll == ls.len)) r.lm = 1ll * ls.sm * rs.lm % mod, r.ll = ls.ll + rs.ll;
else r.lm = ls.lm, r.ll = ls.ll;
if(ls.rop && (rs.rl == rs.len)) r.rm = 1ll * rs.sm * ls.rm % mod, r.rl = rs.rl + ls.rl;
else r.rm = rs.rm, r.rl = rs.rl;
for(int i = 1; i <= r.ctl; i++) r.ct[i] = 0; r.o.clear();
for(int i = 1; i <= sqrt(ls.len); i++) r.ct[i] += ls.ct[i];
for(int i = 1; i <= sqrt(rs.len); i++) r.ct[i] += rs.ct[i];
auto lbg = ls.o.begin(), rbg = rs.o.begin(), led = ls.o.end(), red = rs.o.end();
while(lbg != led && rbg != red){
if(*lbg < *rbg) {if(*lbg <= r.ctl) r.ct[*lbg]++; else r.o.emplace_back(*lbg); lbg++;}
else if(*lbg>*rbg){if(*rbg<=r.ctl) r.ct[*rbg]++; else r.o.emplace_back(*rbg); rbg++;}
else {if(*rbg<=r.ctl) r.ct[*rbg]+=2;else r.o.emplace_back(*rbg),
r.o.emplace_back(*lbg); rbg++; lbg++;}
}while(lbg != led) {if(*lbg <= r.ctl) r.ct[*lbg]++; else r.o.emplace_back(*lbg); lbg++;}
while(rbg != red) {if(*rbg <= r.ctl) r.ct[*rbg]++; else r.o.emplace_back(*rbg); rbg++;}
if(ls.rop){ int tl;
if(ls.rl <= r.ctl) r.ct[ls.rl] --; else r.o.erase(lower_bound(r.o.begin(), r.o.end(), ls.rl));
if(rs.ll <= r.ctl) r.ct[rs.ll] --; else r.o.erase(lower_bound(r.o.begin(), r.o.end(), rs.ll));
tl=ls.rl+rs.ll;if(tl<=r.ctl)r.ct[tl]++;else r.o.insert(lower_bound(r.o.begin(),r.o.end(),tl),tl);
}
}inline void phur(Node &r, Node &ls, Node &rs){
r.len = ls.len + rs.len; r.rop = rs.rop; r.l = ls.l, r.r = rs.r;
if(ls.rop)r.sm=((ls.sm+rs.sm-ls.rm-rs.lm+1ll*ls.rm*rs.lm)%mod+mod)%mod;
else r.sm = (ls.sm + rs.sm) % mod;
if(ls.rop && (ls.ll == ls.len)) r.lm = 1ll * ls.sm * rs.lm % mod, r.ll = ls.ll + rs.ll;
else r.lm = ls.lm, r.ll = ls.ll;
if(ls.rop && (rs.rl == rs.len)) r.rm = 1ll * rs.sm * ls.rm % mod, r.rl = rs.rl + ls.rl;
else r.rm = rs.rm, r.rl = rs.rl;
}inline void ins(int p, int l, int r){
t[p].l = l; t[p].r = r; t[p].len = r - l + 1; t[p].tgv = t[p].tgp = -1;
t[p].ctl = sqrt(t[p].len), t[p].ct = new int[t[p].ctl + 1];
if(l == r){ t[p].ll = t[p].rl = 1; t[p].rop = b[l]; t[p].ad = t[p].ml =
t[p].rv = t[p].lv = t[p].lm = t[p].rm = t[p].sm = a[l]; t[p].ct[1] = 1;return;
}int mid = l + r >> 1; ins(p * 2, l, mid);
ins(p * 2 + 1, mid + 1, r); phun(t[p], t[p * 2], t[p * 2 + 1]);
}inline void pdp(int p, int x){
t[p].rop = t[p].tgp = x;
if(x == 0){
t[p].sm = t[p].ad; t[p].lm = t[p].lv, t[p].rm = t[p].rv;
for(int i = 1; i <= t[p].ctl; i++) t[p].ct[i] = 0; t[p].ll = t[p].rl = 1;
t[p].o.clear(); t[p].ct[1] = t[p].len; return;
}else{
t[p].sm = t[p].ml; t[p].lm = t[p].rm = t[p].ml;
t[p].ll = t[p].rl = t[p].len; t[p].o.clear();
for(int i = 1; i <= t[p].ctl; i++) t[p].ct[i] = 0;
if(t[p].len <= t[p].ctl) t[p].ct[t[p].len]++; else t[p].o.emplace_back(t[p].len);
}
}inline void pdv(int p, int x){ int tp = 1, ps = 0;
t[p].tgv = x; t[p].ad = 1ll * x * t[p].len % mod;
t[p].ml = qpow(x, t[p].len); t[p].lv = t[p].rv = x;
t[p].lm = qpow(x, t[p].ll); t[p].rm = qpow(x, t[p].rl);
t[p].sm = 0; for(int i = 1; i <= t[p].ctl; i++){
if(!t[p].ct[i]) continue; if(ps != i) tp = 1ll * tp * qpow(x, i - ps) % mod, ps = i;
t[p].sm = (t[p].sm + 1ll * t[p].ct[i] * tp % mod) % mod;
} auto bg = t[p].o.begin(), ed = t[p].o.end();
while(bg != ed){
if(*bg != ps) tp = 1ll * tp * qpow(x, *bg - ps) % mod, ps = *bg;
t[p].sm = (t[p].sm + tp) % mod; bg++;}
}inline void phd(int p){
if(t[p].tgv + 1) pdv(p * 2, t[p].tgv), pdv(p * 2 + 1, t[p].tgv);
if(t[p].tgp + 1) pdp(p * 2, t[p].tgp), pdp(p * 2 + 1, t[p].tgp);
t[p].tgp = t[p].tgv = -1;
}inline void upd(int p, int l, int r, int op, int x){
if(t[p].l >= l && t[p].r <= r) {if(op == 1) pdv(p, x); else pdp(p, x); return;}
int mid = t[p].l + t[p].r >> 1; phd(p);
if(l <= mid) upd(p * 2, l, r, op, x); if(r > mid) upd(p * 2 + 1, l, r, op, x);
phun(t[p], t[p * 2], t[p * 2 + 1]);
}inline Node qry(int p, int l, int r){
if(t[p].l >= l && t[p].r <= r) return Node(t[p]);
phd(p); Node ro, ls, rs; int mid = t[p].l + t[p].r >> 1;
if(r <= mid) return qry(p * 2, l, r); if(l > mid) return qry(p * 2 + 1, l, r);
ls = qry(p * 2, l, r); rs = qry(p * 2 + 1, l, r); phur(ro, ls, rs); return ro;
}int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);std::cout.tie(0);
cin >> n >> m; long tp;
for(int i = 1; i <= n; i++) cin >> tp, a[i] = tp % mod;
for(int i = 1; i < n; i++) cin >> b[i]; ins(1, 1, n);
while(m--){ int op, l, r; long x; cin >> op >> l >> r;
if(op == 1) cin >> x, upd(1, l, r, 1, x % mod);
if(op == 2) cin >> x, upd(1, l, r, 2, x);
if(op == 3) cout << qry(1, l, r).sm << '\n';
}
}
gg,果然非常恶心,写了5k之多,足足104行。但是好像在一众题解中算是清爽的但是。
完结撒花。希望能帮你A掉这道黑题。

浙公网安备 33010602011771号