• [Contest on 2021.10.3] 眼珠要爆了


    消失的运算符

    题目描述

    给定一个长度为 (n) 的合法表达式,并且只出现括号,减号和在 ([1,9]) 之间的数字。其中减号代表运算符的空位,设有 (m) 个。
    给定 (k),你要把 (k) 个减号替换成加号,把 (m-k) 个减号替换成乘号。求所有本质不同的替换方案表达式的和对 (10^9+7) 取模后的值。

    (1le nle 10^5,0le mle 2500)

    解法

    ( ext{Subtask 1}):没有括号

    因为要写 (mathcal O(m^2)) 的算法,然后加了前缀和一直调调调,后来发现先开始的 (mathtt{dp}) 方程有问题… 都是泪啊。

    (mathtt{dp}) 转移也很简单,首先找到减号的位置序列 (p),令 (dp_{i,j})(1)(p_i-1) 之间的字符组成的所有表达式中,有 (j) 个加号的权值和。除此之外需要定义辅助数组 (f_{i,j},g_{i,j}),它们的状态和前文类似,但具体分别代表方案数与一坨奇怪的东西(后面再讲。

    那么就有转移:

    [dp_{i,j}=sum_{k=1}^{i-1}dp_{k,j-1}+mul(k+1,i)cdot f_{k,j-1} ]

    由于 (mul(k+1,i)) 这一项很难搞,我们考虑将其拆分成两个部分:令 (pre_i) 为前缀积,(inv_i)(pre_i) 的逆元。转移就可以变成:

    [dp_{i,j}=sum_{k=1}^{i-1}dp_{k,j-1}+pre_icdot sum_{k=1}^{i-1}inv_kcdot f_{k,j-1} ]

    [g_{i,j}=sum_{k=1}^{i-1}inv_kcdot f_{k,j} ]

    那么就有:

    [dp_{i,j}=sum_{k=1}^{i-1}dp_{k,j-1}+pre_icdot g_{i-1,j-1} ]

    就做完了。代码太长了,就放链接吧。

    ( ext{Link.})

    ( ext{Subtask 2}):有括号

    用这个序列构造一棵括号树,相同深度的点所代表的区间就转化成上个子任务的 "数字"。

    但是求几个区间的 (mul()) 似乎比较困难,我们不妨换一个 (mathtt{dp}) 定义:令 (f_{i,j}) 为括号树上的点 (i)(它对应原序列的一段区间),有 (j) 个加号的权值和,(g_{i,j}) 为括号树上的点 (i),有 (j) 个加号的最后一段乘积和,(cnt_{i,j}) 就是方案数。

    具体转移的时候,假设需要算出 (i)(mathtt{dp}) 值,我们先不将 (f_{i,j}) 加上最后一段乘积和。算完再加回去。

    (i) 统治的区间划分成几个上个子任务的 "数字",先分别递归,返回时合并即可,这类似于 树形背包,复杂度是 (mathcal O(n^2)) 的。实现时注意数字 ([l,r]) 对应减号 ([l,r)),在算大小的时候也要考虑。

    代码

    ( ext{Link.})

    数据恢复

    题目描述

    给定一棵 (n) 个点的树,对于所有 (2le ile n),它的父亲为 (f_i),每个点有系数 (a_i,b_i)

    你需要求出一个的排列,满足对于 (2le ile n)(f_i) 都在 (i) 出现前出现。

    这个排列的代价为:

    [sum_{i=1}^n left(b_{p_i}cdot sum_{j=i+1}^n a_{p_j} ight) ]

    求最大代价。

    (nle 3 imes 10^5)

    解法

    菊花图是没有 (f_i) 限制的情况,这提醒我们先思考没有限制怎么做。考虑相邻两个数 (i,j(i<j)),当它们的位置交换时 (delta=b_ja_i-b_ia_j)。假设交换前更优,就可以得出 (frac{a_i}{b_i}<frac{a_j}{b_j}),令 (v_i=frac{a_i}{b_i}),直接升序排然后选即可。

    加上限制之后,还是升序排序,可以发现选 (f_u) 之后直接选 (u) 肯定是最优的。而且,由乘法分配律,我们惊喜地发现这两个点的信息是可以合并的!用一个堆维护升序,就是 (mathcal O(nlog n)) 的。

    另一道类似的题是 牛半仙的魔塔

    由于奇特的数据范围,你会发现牛半仙每次减少的血量都是正数,所以我们只需要最小化减少的血量即可(因为不会回血,所以末状态一定是血量最低时刻)。

    最小化减少的血量似乎挺复杂的,其实还有一个性质:每个怪物存活的轮数是固定的。从而每个怪物对牛半仙的伤害是固定的(如果防御不增加的话)。所以最大化防御值减伤的效果即可。假设怪物 (i) 的存活轮数是 (r_i),增加防御为 (w_i)。还是考虑相邻两个数 (i,j(i<j)),当它们的位置交换时 (delta=w_jr_i-w_ir_j)(减伤的效果)。假设交换前更优,就可以得出 (frac{w_i}{r_i}>frac{w_j}{r_j}),令 (v_i=frac{w_i}{r_i}),这就变成了上面的问题。实际实现 还要模拟一下打怪。

    代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f |= (s=='-');
    	while(s>='0' and s<='9')
    		x = (x<<1)+(x<<3)+(s^48),
    		s = getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <queue>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 3e5+5;
    
    int n,fa[maxn],f[maxn];
    int a[maxn],b[maxn],siz[maxn];
    struct node {
    	double v;
    	int id,sz;
    	node() {}
    	node(double V,int ID,int SZ) {
    		v=V,id=ID,sz=SZ;
    	}
    	
    	bool operator < (const node &t) const {
    		return v>t.v;
    	}
    };
    priority_queue <node> q;
    
    void init() {
    	for(int i=1;i<=n;++i)
    		f[i]=i,siz[i]=1;
    }
    
    int Find(int x) {
    	return x==f[x]?x:f[x]=Find(f[x]);
    }
    
    int main() {
    	n=read(9); init();
    	for(int i=2;i<=n;++i)
    		fa[i]=read(9);
    	for(int i=1;i<=n;++i) {
    		a[i]=read(9),
    		b[i]=read(9);
    		if(i^1)
    			q.push(node(1.0*a[i]/b[i],i,1));
    	}
    	int x,y; ll ans=0;
    	while(!q.empty()) {
    		node t=q.top(); q.pop();
    		if(t.sz^siz[t.id])	
    			continue;
    		x=Find(fa[t.id]);
    		y=Find(t.id);
    		if(x==y) continue;
    		ans += 1ll*b[x]*a[y];
    		b[x]+=b[y],a[x]+=a[y];
    		siz[f[y]=x]+=siz[y];
    		if(x^1)
    			q.push(node(1.0*a[x]/b[x],x,siz[x]));
    	}
    	print(ans,'
    ');
    	return 0;
    }
    

    下落的小球

    题目描述

    有一棵 (n) 个点的树,根为 (1),每个点都有一个初始为 (1) 的标记值 (s_i)。对点 (x) 进行操作表示把 (x) 的祖先中深度最小并且 (s_i=1)(i) 置为 (0)

    每个 叶子 有一个操作次数 (a_i),问有多少种不同的操作序列使得最后标记值全为 (0)

    (nleq 10^6)(sum a_i=n),保证若 (i) 非叶子则 (a_i=0)

    解法

    (b_i)(i) 子树的 (a_i) 之和,(s_i) 为子树大小。令 (r_i=b_i-s_i),如果任意 (i) 均满足 (r_ige 0),这个 (a) 序列就是可以实现的。具体就是若 (r_i<0),那么这棵子树不会被消耗完;反之,由于题目中保证了 (sum a_i=n),所以不会出现 "供不应求" 的情况。

    对于点 (i),考虑何时它的标记值为 (0)。走 (r_i) 步后,子树中每个点的标记值均为 (1),且此时子树操作次数也变成 (s_i),显然不再需要外部供给了。所以 (r_i+1) 步时 (i) 的标记值就是 (0) 了。将 (r_i) 步之内称为 (S) 部,剩余步称为 (T) 部。

    套路地,我们从子树到根合并某节点儿子 (x,y) 的操作序列,令点 (i) 的操作序列方案数为 (dp_i)。由于共用父亲,当 (x) 失去外部供给时 (y) 还有外部供给显然是不合理的,所以 (x,y)(S,T) 部一定是 (S_x,S_y) 自由组合,(T_x,T_y) 自由组合再将 (S',T')顺序 拼接。这就是个组合数嘛!

    具体可以这样转移:

    for(v in Son_u) {
        s[u]+=s[v],r[u]+=r[v];
        dp[u] = dp[u]*dp[v]/(s[v]!)/(r[v]!);
    }
    dp[u] = dp[u]*(s[u]-1)!*(r[u]+1)!;
    

    dp[u]*dp[v] 是选出 各自 排列顺序,剩下一坨就是组合数,化简发现中间一段可以抵消。由于 (u) 不是叶子,所以 (sum r_v=r_u+1)

    古老的序列问题

    题目描述

    给定长度为 (n) 的序列 (s)。你需要回答 (m) 个询问,每次询问给出 (L,R),你需要计算以下式子:

    [left (sum_{l=L}^R sum_{r=l}^R cost(l,r) ight)mod 10^9+7 ]

    其中 (cost(l,r))([l,r]) 中最大值和最小值的乘积。

    (1le n,mle 10^5,1le s_ile 10^9)

    解法

    ( ext{Subtask 1})(s_ile s_{i+1})

    问题相当于求 ([L,R]) 中两两数字的乘积。可以预处理出 (suf_i=s_icdot sum_{j=i}^n s_i)。那么

    [ ext{Ans}=sum_{i=L}^R suf_i -sum_{i=L}^Rs_icdot sum_{i=R+1}^n s_i ]

    另外中间还有几档部分分,但是我不会~

    ( ext{Subtask 2})(mathcal O(nlog^2 n))

    感觉这种离线询问题很多都可以用 ( m cdq) 分治搞一个最优/次优解出来,以后不妨往上面靠靠… 当然这道题就是打死我也想不到该怎么搞

    一个粗糙的思路是,将完全在 ([l, ext{mid}))(( ext{mid},r]) 的询问扔给儿子,自己处理经过 ( ext{mid})(cost(l',r'))

    ( m mid) 开始向左移左指针,按这个顺序对右区间贡献(用数据结构维护)。这样我们将询问按 (L) 从大到小排序,就可以当左指针到 (L_i) 时,更新询问 (i)。我们把经过 ( m mid) 的区间划分成四种情况:

    1. (min,max) 都在左区间。
    2. (max) 在左区间。
    3. (min) 在左区间。
    4. (min,max) 都在右区间。

    由于左指针左移时,左区间的 (min,max) 一定趋向更优,所以可以用两个指针维护右区间满足 "(min,max) 都在左区间" 的右端点的边界 ( ext{MX}, ext{MN}),这是递增的。然后就可以得到四种情况的区间。

    讲了这么多,究竟该如何维护 (cost()) 呢?建出四棵线段树分别代表四种情况(维护右区间),比如,情况二初始时就是给 (( ext{mid},r])(v_j) 赋值为 (min_{i= ext{mid}+1}^j s_j),在移动左指针时,找到情况二对应的区间,然后传递当前左区间的 (max),区间内每个点的答案就需要累加 (v_jcdot max)

    感觉每一步都好妙啊。

    ( ext{Subtask 3})(mathcal O(nlog n))

    事实上,这种 "给定区间求所有子序列信息" 的题目有一个通用的思路:求历史的和。

    比如此题,我们右移右端点到 (i),此时左端点 ([1,i]) 表示 (cost({1,2,...,i},i))。当 (i+1) 时,(cost({1,2,...,i},i)) 就变成了历史值。对于询问 ((l,r)),我们就需要求出右端点为 (r) 时,区间 ([l,r]) 的历史值。

    可以用单调栈维护某个区间的 (min,max)(下文以 (min) 为例)。在右端点移动时,左端点对应的 (min) 可能会变化,如何修改?由于我们已经知道需要修改 (min) 的区间,所以直接乘上原本 (min) 的逆元即可。

    具体可以用矩阵维护,令初始矩阵为 (egin{bmatrix}v&h end{bmatrix})。第一想法是将转移矩阵写成这样:(egin{bmatrix} c& ext{len}\ 0&1end{bmatrix})。然而修改时我们并不知道区间的 ( m len),所以将其设在初始矩阵的 (v) 处即可。需要注意的是,修改后没有将历史和更新,所以需要再乘一个矩阵更新历史和。

    这就是 (mathcal O(nlog n)) 的,实测比上面那个做法快了一倍有余,只有 (600) 多毫秒。

    代码

    (mathcal O(nlog^2 n))

    #include <vector>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int maxn = 1e5+5;
    const int mod = 1e9+7;
    
    inline int inc(int x,int y) {
    	return x+y>=mod?x+y-mod:x+y;
    }
    
    int n,m,s[maxn],f[maxn<<2];
    int ans[maxn],L,R;
    int a_[maxn],b_[maxn],c_[maxn],d_[maxn];
    struct node {
    	int l,r,id;
    	node() {}
    	node(int L,int R,int ID):l(L),r(R),id(ID) {}
    	
    	bool operator < (const node &t) const {
    		return l>t.l;
    	}
    };
    vector <node> q[maxn<<2];
    
    class SegmentTree {
    public:
    	int la[maxn<<2],v[maxn<<2],sm[maxn<<2];
    	
    	void build(int o,int l,int r,int *t) {
    		la[o]=sm[o]=0;
    		if(l==r) return v[o]=t[l],void();
    		int mid=(l+r)>>1;
    		build(o<<1,l,mid,t);
    		build(o<<1|1,mid+1,r,t);
    		v[o]=inc(v[o<<1],v[o<<1|1]);
    	}
    	
    	void update(int o,int k) {
    		sm[o] = inc(sm[o],1ll*v[o]*k%mod);
    		la[o] = inc(la[o],k);
    	}
    	
    	void pushDown(int o) {
    		if(!la[o]) return;
    		update(o<<1,la[o]);
    		update(o<<1|1,la[o]);
    		la[o]=0;
    	}
    	
    	void modify(int o,int ql,int qr,int k,int l=L,int r=R) {
    		if(l>=ql and r<=qr)
    			return update(o,k),void();
    		int mid=(l+r)>>1;
    		pushDown(o);
    		if(ql<=mid) modify(o<<1,ql,qr,k,l,mid);
    		if(qr>mid) modify(o<<1|1,ql,qr,k,mid+1,r);
    		sm[o] = inc(sm[o<<1],sm[o<<1|1]);
    	}
    	
    	int ask(int o,int lim,int l=L,int r=R) {
    		if(r<=lim) return sm[o];
    		int mid=(l+r)>>1,ret;
    		pushDown(o);
    		ret=ask(o<<1,lim,l,mid);
    		if(mid<lim) 
    			ret=inc(ret,ask(o<<1|1,lim,mid+1,r));
    		return ret;
    	}
    } A,B,C,D;
    
    void dicon(int o,int l,int r) {
    	if(l==r) {
    		f[o] = 1ll*s[l]*s[l]%mod;
    		for(auto i:q[o])
    			ans[i.id] = inc(ans[i.id],f[o]);
    		return;
    	}
    	int mid=(l+r)>>1;
    	vector <node> cur;
    	for(auto i:q[o]) {
    		if(i.l<=mid and i.r>mid and !(i.l==l and i.r==r))
    			cur.push_back(i);
    		if(i.r<=mid) q[o<<1].push_back(i);
    		else if(i.l>mid) q[o<<1|1].push_back(i);
    		else if(!(i.l==l and i.r==r)) {
    			q[o<<1].push_back(node(i.l,mid,i.id));
    			q[o<<1|1].push_back(node(mid+1,i.r,i.id));
    		}
    	}
    	sort(cur.begin(),cur.end());
    	int mn=mod,mx=0;
    	for(int i=mid+1;i<=r;++i) {
    		mn=min(mn,s[i]);
    		mx=max(mx,s[i]);
    		a_[i]=1; // all in left
    		b_[i]=mn; // max in left
    		c_[i]=mx; // min in left
    		d_[i]=1ll*mx*mn%mod;
    		// all in right
    	}
    	L=mid+1,R=r;
    	A.build(1,mid+1,r,a_); B.build(1,mid+1,r,b_);
    	C.build(1,mid+1,r,c_); D.build(1,mid+1,r,d_);
    	int p=0,siz=cur.size(),MN=mid,MX=mid;
    	mn=mod,mx=0;
    	for(int i=mid;i>=l;--i) {
    		mn=min(mn,s[i]);
    		mx=max(mx,s[i]);
    		while(MN<r and s[MN+1]>=mn) ++MN;
    		while(MX<r and s[MX+1]<=mx) ++MX;
    		if(mid+1<=MN and mid+1<=MX)
    			A.modify(1,mid+1,min(MN,MX),1ll*mn*mx%mod);
    		if(MN<MX) B.modify(1,MN+1,MX,mx);
    		if(MN>MX) C.modify(1,MX+1,MN,mn);
    		if(MX+1<=r and MN+1<=r)
    			D.modify(1,max(MN,MX)+1,r,1);
    		while(p<siz and cur[p].l==i) {
    			ans[cur[p].id] = inc(
    				ans[cur[p].id],
    				inc(
    					inc(A.ask(1,cur[p].r),B.ask(1,cur[p].r)),
    					inc(C.ask(1,cur[p].r),D.ask(1,cur[p].r))
    				)
    			);
    			++p;
    		}
    	}
    	f[o] = inc(inc(A.ask(1,r),B.ask(1,r)),
    			   inc(C.ask(1,r),D.ask(1,r)));
    	dicon(o<<1,l,mid),dicon(o<<1|1,mid+1,r);
    	f[o] = inc(f[o],inc(f[o<<1],f[o<<1|1]));
    	for(auto i:q[o])
    		if(i.l==l and i.r==r)
    			ans[i.id] = inc(ans[i.id],f[o]);
    }
    
    int main() {
    	n=read(9),m=read(9);
    	for(int i=1;i<=n;++i)
    		s[i]=read(9);
    	int x,y;
    	for(int i=1;i<=m;++i)
    		x=read(9),y=read(9),
    		q[1].push_back(node(x,y,i));
    	dicon(1,1,n);
    	for(int i=1;i<=m;++i)
    		print(ans[i],'
    ');
    	return 0;
    }
    

    (mathcal O(nlog n))

    #pragma GCC optimize(2)
    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f |= (s=='-');
    	while(s>='0' and s<='9')
    		x = (x<<1)+(x<<3)+(s^48),
    		s = getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-');
    		write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <vector>
    using namespace std;
    
    const int maxn = 1e5+5;
    const int mod = 1e9+7;
    
    inline int inc(int x,int y) {
    	return x+y>=mod?x+y-mod:x+y;
    }
    
    int n,m,s[maxn],ans[maxn];
    int mn[maxn],mn_l,mx[maxn],mx_l;
    struct Q {
    	int l,id;
    	Q() {}
    	Q(int L,int ID):l(L),id(ID) {}
    };
    vector <Q> q[maxn];
    struct mat {
    	int a[2][2];
    	mat() {
    		a[0][0]=a[0][1]=a[1][0]=a[1][1]=0;
    	}
    	
    	mat operator * (const mat &t) const {
    		mat r;
    		for(int i=0;i<2;++i)
    		for(int j=0;j<2;++j)
    			if(a[i][j])
    				for(int k=0;k<2;++k)
    					r.a[i][k]=inc(
    						r.a[i][k],
    						1ll*a[i][j]*t.a[j][k]%mod
    					);
    		return r;
    	}
    	
    	bool operator == (const mat &t) const {
    		for(int i=0;i<2;++i)
    		for(int j=0;j<2;++j)
    			if(a[i][j]^t.a[i][j])
    				return 0;
    		return 1;
    	}
    	
    	void Print() {
    		puts("----------");
    		for(int i=0;i<2;++i) {
    			for(int j=0;j<2;++j)
    				print(a[i][j],' ');
    			puts("");
    		}
    	}
    } e,upd,k,t[maxn<<2],la[maxn<<2];
    
    int inv(int x,int y=mod-2) {
    	int r=1;
    	while(y) {
    		if(y&1) r=1ll*r*x%mod;
    		x=1ll*x*x%mod; y>>=1;
    	}
    	return r;
    }
    
    void initMat() {
    	e.a[0][0]=e.a[1][1]=1;
    	upd.a[0][0]=upd.a[1][1]=upd.a[0][1]=1;
    }
    
    void getMat(int x) {
    	k.a[0][0]=x,k.a[1][1]=1;
    	k.a[0][1]=k.a[1][0]=0;
    }
    
    void build(int o,int l,int r) {
    	t[o].a[0][0]=r-l+1;
    	la[o]=e; // important!
    	if(l==r) return;
    	int mid=l+r>>1;
    	build(o<<1,l,mid);
    	build(o<<1|1,mid+1,r);
    }
    
    void pushDown(int o) {
    	if(la[o]==e) return;
    	t[o<<1]=t[o<<1]*la[o];
    	t[o<<1|1]=t[o<<1|1]*la[o];
    	la[o<<1]=la[o<<1]*la[o];
    	la[o<<1|1]=la[o<<1|1]*la[o];
    	la[o]=e;
    }
    
    void modify(int o,int l,int r,int L,int R) {
    	if(l>=L and r<=R) {
    		t[o]=t[o]*k; la[o]=la[o]*k;
    		return;
    	}
    	int mid=l+r>>1;
    	pushDown(o);
    	if(L<=mid) modify(o<<1,l,mid,L,R);
    	if(R>mid) modify(o<<1|1,mid+1,r,L,R);
    	t[o].a[0][0]=inc(t[o<<1].a[0][0],t[o<<1|1].a[0][0]);
    	t[o].a[0][1]=inc(t[o<<1].a[0][1],t[o<<1|1].a[0][1]);
    }
    
    int ask(int o,int l,int r,int L,int R) {
    	if(l>=L and r<=R) return t[o].a[0][1];
    	int mid=l+r>>1,ret=0;
    	pushDown(o);
    	if(L<=mid) ret=ask(o<<1,l,mid,L,R);
    	if(R>mid) ret=inc(ret,ask(o<<1|1,mid+1,r,L,R));
    	return ret;
    }
    
    int main() {
    	n=read(9),m=read(9);
    	for(int i=1;i<=n;++i)
    		s[i]=read(9);
    	int x,y;
    	for(int i=1;i<=m;++i)
    		x=read(9),y=read(9),
    		q[y].push_back(Q(x,i));
    	initMat(); build(1,1,n); 
    	for(int i=1;i<=n;++i) {
    		while(mn_l and s[mn[mn_l]]>=s[i]) { 
    			getMat(inv(s[mn[mn_l--]])); 
    			modify(1,1,n,mn[mn_l]+1,mn[mn_l+1]);
    		}
    		while(mx_l and s[mx[mx_l]]<=s[i]) {
    			getMat(inv(s[mx[mx_l--]]));
    			modify(1,1,n,mx[mx_l]+1,mx[mx_l+1]);
    		}
    		getMat(s[i]);
    		modify(1,1,n,mx[mx_l]+1,i);
    		modify(1,1,n,mn[mn_l]+1,i);
    		k=upd;
    		modify(1,1,n,1,i);
    		mx[++mx_l]=mn[++mn_l]=i;
    		for(int j=0;j<q[i].size();++j)
    			ans[q[i][j].id]=ask(1,1,n,q[i][j].l,i);
    	}
    	for(int i=1;i<=m;++i) print(ans[i],'
    ');
    	return 0;
    }
    
  • 相关阅读:
    Django form
    八月22日,django知识点总结:
    MVC其实很简单(Django框架)
    Django框架
    python实战(开发新浪微博应用)
    MySQL练习题
    MySQL练习题参考答案
    Python中用format函数格式化字符串
    harbor的nginx替换
    bash传参的使用记录
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15367511.html
Copyright © 2020-2023  润新知