dp 练习 2024.4.9/4.10

Luogu8389 [ZJOI2022] 树#

考虑容斥,每个点有三个状态:不钦定、钦定都是叶子、钦定都是非叶子。对于每一种钦定状态都计算答案,然后乘上对应容斥系数即可。

叶子的钦定是可做的,考虑非叶子的钦定,我们可以用“容斥套容斥”的思想理解:对于钦定都是非叶子的点 i,考虑用总方案数减去钦定其中之一是叶子的方案数,再加上两者都是叶子的方案数。

把非叶子的钦定放回原容斥里描述:减去总方案数,加上钦定第一棵树中是叶子的方案数、加上钦定第二棵树中是叶子的方案数、减去两棵树中都是叶子的方案数。

和不钦定、钦定叶子的方案加起来,相当于:加上钦定第一棵树中是叶子的方案数、加上钦定第二棵树中是叶子的方案数、减去 2× 两棵树中都是叶子的方案数。

f[i,j,k] 表示考虑了前 i 个点,其中第一棵树中有 j 个没有被钦定是叶子的点,第二棵树中还剩 k 个没有被钦定是叶子的点。

转移容易。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn=510, M=1e9;
ll n,mod,f[2][maxn][maxn],ans;
int main(){
	scanf("%lld%lld",&n,&mod);
	for(ll i=1;i<n;i++) f[1][1][i]=i;
	puts("1");
	for(ll i=2;i<n;i++){
		memset(f[i&1],0,sizeof f[i&1]);
		for(ll j=1;j<=n;j++)
			for(ll k=1;k<=n;k++){
				if(!f[i&1^1][j][k]) continue;
				f[i&1][j][k-1]=(f[i&1][j][k-1]+f[i&1^1][j][k]*j*(k-1))%mod;
				f[i&1][j+1][k]=(f[i&1][j+1][k]+f[i&1^1][j][k]*j*k)%mod;
				f[i&1][j][k]=(f[i&1][j][k]-2*f[i&1^1][j][k]*j*k)%mod;
			}
		ans=0;
		for(ll j=1;j<=i;j++)
			ans=(ans+f[i&1][j][1]*j)%mod;
		printf("%lld\n",(ans+mod)%mod);
	}
	return 0;
}

AGC036F Square Constraints#

相当于有两个圆,每个位置的取值必须在两个圆中间,求方案数。

本质上还是 pi[li,ri] 的计数。考虑如果没有下面那个圆怎么做,把 ri 从小到大排序,答案就是 i(rii+1)

如果有下面的圆,因为钦定后本质上还是在一段前缀上取值,我们考虑容斥

钦定取值在下面圆内的位置集合 S,不难发现 S{0,2,...,n1},考虑使用 dp 来综合所有的 S

但是这样有个问题,我们难以计算方案数。尝试先排序:对于 i=n...2n1,基准为 ri;对于 i=0...n1,基准为 li1。按基准来排序。

对于未被钦定的位置 i,我们还要知道 ri 前面有多少个比他小的数,发现我们只要知道了钦定的位置个数就能算出。因为对于每个钦定的位置 i(i<n)li1 总是不大于所有未钦定的位置 j(j<n)rj

所以我们考虑 dp 开始前先枚举钦定的位置个数,便于计算。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn=510;
ll n,id[maxn],mod,f[maxn][maxn],ans,l[maxn],r[maxn];
bool cmp(ll x,ll y){ ll p=(r[x]<r[y]);
	x=(x<n? l[x]-1:r[x]), y=(y<n? l[y]-1:r[y]);
	return x^y? x<y:p;
}
int main(){
	scanf("%lld%lld",&n,&mod);
	for(ll i=0;i<(n<<1);i++){
		if(i<n) l[i]=ceil(sqrt(n*n-i*i));
		r[i]=sqrt(4*n*n-i*i); id[i+1]=i;
		r[i]=min(r[i],(n<<1)-1);
	} sort(id+1,id+1+(n<<1),cmp);
	for(ll d=0;d<=n;d++){ //printf("d = %lld\n",d);
		memset(f,0,sizeof f);
		f[0][0]=1; ll cnt=0;
		for(ll i=1;i<=(n<<1);i++){
			for(ll j=0;j<=d&&j<=i-1-cnt;j++){
				if(id[i]>=n){
					if(cnt+j<=r[id[i]]) f[i][j]=(f[i][j]+f[i-1][j]*(r[id[i]]+1-cnt-j))%mod;
				}
				else{
					if(n+d+(i-1-cnt-j)<=r[id[i]]) f[i][j]=(f[i][j]+f[i-1][j]*(r[id[i]]+1-n-d-(i-1-cnt-j)))%mod;
					if(j<d&&cnt+j<l[id[i]]) f[i][j+1]=(f[i][j+1]+f[i-1][j]*(l[id[i]]-cnt-j))%mod;
				}
			} cnt+=(id[i]>=n);
		}
		ans=(ans+(d&1? mod-1:1)*f[n<<1][d])%mod;
	} printf("%lld",ans);
	return 0;
}

AGC040E Prefix Suffix Addition#

考虑原问题的本质:将 xi 分裂为 ai+bi,钦定 a0=bn+1=0,最小化 i=1n[ai1>ai]+i=1n[bi<bi+1]

挖掘问题的本质很重要,这样能转化模型。

我们暴力,设 f[i,j] 表示确定了 a1...i,b1...i,并且 ai=j 的答案。

转移很显然:

f[i,j]=min0kxi1{[k>j]+[xi1k<xij]+f[i1,k]}

  • 一个 trick:注意每次转移最多加 2,并且都是全局贡献,那么 f[i,_] 最多 3 段,直接维护。

val=min0kxi1f[i1,k],且位置为 p,不难发现 f[i,max(p,xi(xi1p))...xi] 都会变成 val

接着处理 val+1 的段,是 [min(p,xi(xi1p)),max(p,xi(xi1p)))

剩下那段是前缀,等于 val+2

顺便可以发现,我们的 p 是贪心取最小的位置,我们直接维护 p1,p2,p3 表示三段的起点即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn=2e5+10, mod=998244353, iv=mod-mod/2;
ll n,a[maxn],p1,p2,p3,val;
int main(){
	scanf("%lld",&n);
	p1=p2=p3=val=0;
	for(ll i=1;i<=n+1;i++){
		if(i<=n) scanf("%lld",a+i);
		ll q3=min(a[i]+1,max(p3,a[i]-(a[i-1]-p3))),
		q2=max(0ll,min(min(p3,a[i]-(a[i-1]-p3)),max(p2,a[i]-(a[i-1]-p2)))), q1=0, v=val;
		if(q3>a[i]) q3=q2, q2=q1, q1=0, ++v;
		p1=q1, p2=q2, p3=q3, val=v;
	} printf("%lld",val);
	return 0;
}

AGC022F Checkers#

详见 AGC022F 做题记录

CF1930G Prefix Max Set Counting#

直接 DP 最终序列,然后思考充要条件

f[u] 表示前缀 max 序列接到 u 的方案数。

pu 表示 1u 路径上的最大点,当 pu>uu 显然不可能在序列中。

否则思考上一个点 v,首先可以是 v=pu,直接从 pu 走到 u

如果不是,v 必定不是 u 的祖先。那么我们会发现一个条件,v[pu+1,u1],这个容易脑补证明。

思考还有什么条件,设 clca(u,v),那么 vc 的包含 v 的儿子的子树内编号最大点。

这个比较显然,我们一定是先 dfs 完那棵子树再出来的。

然后我们会发现这些转移也是充分的,在这种条件下,我们不可能重复走进同一棵子树。

所以可以直接 DP。首先,v=pu 的转移是容易的。

然后思考 v 不是祖先的情况,我们考虑求出 f[v] 时,不断祖先链,跳到 x 时需满足 vx 子树内最大点,然后在 fax 上加上 f[v] 的贡献。

这样一来,我们只需要求 u 所有祖先上面的贡献之和,树状数组即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn=1e6+10, mod=998244353;
ll t,n,fa[maxn],mx[maxn],pre[maxn],tree[maxn],dfn[maxn],out[maxn],ti,f[maxn];
vector<ll>to[maxn],vec[maxn];
void dfs(ll u,ll ff){
	dfn[u]=++ti;
	fa[u]=ff; pre[u]=max(pre[ff],u);
	mx[u]=u;
	for(ll v:to[u])
		if(v!=ff) dfs(v,u), mx[u]=max(mx[u],mx[v]);
	out[u]=ti;
}
void add(ll x,ll v){
	while(x<=n){
		tree[x]=(tree[x]+v)%mod;
		x+=x&-x;
	}
}
ll ask(ll x){
	ll v=0;
	while(x){
		v=(v+tree[x])%mod;
		x&=x-1;
	} return v;
}
int main(){
	scanf("%lld",&t);
	while(t--){
		scanf("%lld",&n);
		for(ll i=1;i<n;i++){
			ll u,v; scanf("%lld%lld",&u,&v);
			to[u].pb(v), to[v].pb(u);
		}
		dfs(1,0);
		f[1]=1;
		for(ll i=2;i<=n;i++)
			if(pre[fa[i]]<i) vec[pre[fa[i]]].pb(i);
		for(ll u=2;u<=n;u++){
			if(pre[fa[u]]>u) continue;
			f[u]+=f[pre[fa[u]]], f[u]%=mod;
			f[u]=(f[u]+ask(dfn[u]))%mod;
			for(ll x:vec[u]) f[x]=mod-ask(dfn[x]);
			ll x=u;
			while(x>1&&mx[x]==u){
				add(dfn[fa[x]],f[u]), add(out[fa[x]]+1,mod-f[u]);
				x=fa[x];
			}
		}
		printf("%lld\n",f[n]);
		for(ll i=1;i<=n;i++){
			to[i].clear(), vec[i].clear();
			tree[i]=f[i]=0;
		} ti=0;
	}
	return 0;
}

AGC020F Arcs on a Circle#

直接做很困难。

注意到所有弧长都是整数,他们的起始位置的小数部分期望下分别为 1n+1,2n+1,...,nn+1,由于 n 很小,暴力枚举全排列。

我们考虑断环为链,这里有个比较技巧的东西,以最长弧的起点断开,这样前面的弧一定包含不到这条弧,那么最后一个位置延伸过去的弧就不用管。

这样弧就变成了线段。由于每个线段的起点的小数部分已经确定,我们把 c 个点拆成 n×c 个点,每个点都只能由一条线段为开头。

直接 dp,设 f[i,j,S] 表示考虑了起点 k 的所有线段,覆盖到了点 j,使用了的线段集合为 S,的概率。

转移是容易的。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn=52, mod=1e9+7;
ll N,C,L[maxn],p[maxn];
double f[310][310][1<<6],ans;
int main(){
	scanf("%lld%lld",&N,&C);
	for(ll i=1;i<=N;i++) scanf("%lld",L+i);
	sort(L+1,L+1+N);
	for(ll i=2;i<=N;i++) p[i]=i-1;
	do{
		memset(f,0,sizeof f);
		f[1][L[N]*N+1][0]=1;
		for(ll i=2;i<=N*C;i++){
			for(ll j=i;j<=N*C+1;j++){
				for(ll S=0;S<(1<<N-1);S++){
					if(i<j) f[i][j][S]+=f[i-1][j][S];
					ll x=p[(i-1)%N+1];
					if(x&&!(S&(1<<x-1))){
						f[i][min(N*C+1,max(j,i+L[x]*N))][S|(1<<x-1)]+=f[i-1][j][S]/C;
					}
				}
			}
		}
		ans+=f[N*C][N*C+1][(1<<N-1)-1];
	}while(next_permutation(p+2,p+N+1));
	for(ll i=1;i<N;i++) ans/=i;
	printf("%.15lf",ans);
	return 0;
}

CF1466H Finding satisfactory solutions#

详见 CF1466H 做题记录


Luogu4426 [HNOI/AHOI2018] 毒瘤#

终于是一道比较 trival 的题了。

考虑广义串并联图的做法,会发现在此处仍然使用:

  • 删一度点

  • 缩二度点

每删掉一个点后,会带走一条边,所以最终是形如 x 个点,x+k 条边的图,其中 k=mn

最终每个点度数都 3,所以 3x2(x+k),解得 x2k,即 x20

所以可以直接暴力枚举每个点的状态。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn=2e5+10, mod=998244353;
struct Data {ll dp[2][2];};
set<ll>to[maxn];
unordered_map<ll,Data>w[maxn];
ll n,m,q[maxn],l,r,ans,g[maxn][2],vis[maxn],id[maxn],rk[maxn],len;
void ins(ll u,ll v,Data t){
	if(to[u].count(v)){
		Data p=w[u][v];
		p.dp[0][0]=p.dp[0][0]*t.dp[0][0]%mod;
		p.dp[0][1]=p.dp[0][1]*t.dp[0][1]%mod;
		p.dp[1][0]=p.dp[1][0]*t.dp[1][0]%mod;
		p.dp[1][1]=p.dp[1][1]*t.dp[1][1]%mod;
		w[u][v]=p;
		swap(p.dp[0][1],p.dp[1][0]), w[v][u]=p;
	} else{
		to[u].insert(v), to[v].insert(u);
		w[u][v]=t;
		swap(t.dp[0][1],t.dp[1][0]), w[v][u]=t;
	}
}
int main(){
	scanf("%lld%lld",&n,&m);
	for(ll i=1;i<=m;i++){
		ll u,v; scanf("%lld%lld",&u,&v);
		to[u].insert(v), to[v].insert(u);
		w[u][v]=w[v][u]=(Data){{{1,1},{1,0}}};
	}
	for(ll i=1;i<=n;i++) g[i][0]=g[i][1]=1;
	for(ll i=1;i<=n;i++)
		if(to[i].size()<=2) q[++r]=i, vis[i]=1;
	l=1; vis[q[n]]=0, r=min(r,n-1);
	while(l<=r){
		ll u=q[l++];
		if(to[u].size()==1){
			ll v=*to[u].begin(); to[u].clear(), to[v].erase(u);
			Data tmp=w[u][v];
			g[v][0]=(g[u][0]*tmp.dp[0][0]+g[u][1]*tmp.dp[1][0])%mod*g[v][0]%mod;
			g[v][1]=(g[u][0]*tmp.dp[0][1]+g[u][1]*tmp.dp[1][1])%mod*g[v][1]%mod; 
			if(r+1<n&&to[v].size()<=2&&!vis[v]) q[++r]=v, vis[v]=1;
		} else{
			ll v1=*to[u].begin(), v2=*to[u].rbegin();
			to[u].clear(), to[v1].erase(u), to[v2].erase(u);
			Data t1=w[v1][u], t2=w[u][v2];
			Data t;
			t.dp[0][0]=(t1.dp[0][0]*t2.dp[0][0]%mod*g[u][0]+t1.dp[0][1]*t2.dp[1][0]%mod*g[u][1])%mod;
			t.dp[0][1]=(t1.dp[0][0]*t2.dp[0][1]%mod*g[u][0]+t1.dp[0][1]*t2.dp[1][1]%mod*g[u][1])%mod;
			t.dp[1][0]=(t1.dp[1][0]*t2.dp[0][0]%mod*g[u][0]+t1.dp[1][1]*t2.dp[1][0]%mod*g[u][1])%mod;
			t.dp[1][1]=(t1.dp[1][0]*t2.dp[0][1]%mod*g[u][0]+t1.dp[1][1]*t2.dp[1][1]%mod*g[u][1])%mod;
			ins(v1,v2,t);
		}
	}
	for(ll i=1;i<=n;i++)
		if(!vis[i]) id[++len]=i, rk[i]=len;
	for(ll S=0;S<(1<<len);S++){ ll res=1;
		for(ll i=1;i<=len;i++){
			ll u=id[i]; res=res*g[u][(S>>i-1)&1]%mod; 
			for(set<ll>::iterator it=to[u].begin();it!=to[u].end();it++){
				ll v=*it;
				if(v>u) res=res*w[u][v].dp[(S>>i-1)&1][(S>>rk[v]-1)&1]%mod;
			}
		} ans=(ans+res)%mod;
	} printf("%lld",ans);
	return 0;
}

AGC012F Prefix Median#

AGC,考虑充要条件,直接用充要条件计数。

先假设 a1...2n1 两两不同。

不妨先对 a 从小到大排序,首先可以观察到,前 2i1 个数的中位数只能在 ai...ni+1 中选,就好比 2n1 个数的中位数只能是 an

我们不妨倒着进行整个过程,对于 2n12(n1)1,中位数至多移动一位。什么意思,换句话说,前 2(n1)1 个数的中位数为 an1/an/an+1

如果前 2(n1)1 个数中位数为 an+1,那么对于前 2(n2)1 个数,中位数可以是 an+2/an+1/an/an1,注意 an1 也可以选,因为我们在 2n12(n1)1 的过程中,an 可能被删除。

这下结论很好猜了:对于 i,不存在 j<i 使得 bj 夹在 bi,bi+1 两数之间。

把充要条件结合图像:一开始对于 2n1,只有一个黑点,并且一个小人在黑点上;每次往前枚举一位,两边各新增一个黑点,然后小人可以跳到任意一个黑点,并把两个黑点中间的黑点全部删掉,求跳跃方案数。

这个 dp 是容易的,设 f[i,j,k] 表示处理了 i...n 轮,并且有 j 个黑点,小人在第 k 个黑点上。

考虑 a1...2n1 有相同的情况,不难发现,数值相同的黑点其实没有意义,我们只需要保留其中一个即可。

因此,当新拓展的黑点数值和原来有的黑点一样时,拓展是不必要的,直接 dp 也是容易的。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn=110, mod=1e9+7;
ll n,a[maxn],f[maxn][maxn][maxn],b[maxn],ans;
void add(ll &x,const ll &y) {x=(x+y>=mod? x+y-mod:x+y);}
int main(){
	scanf("%lld",&n);
	for(ll i=1;i<(n<<1);i++){
		scanf("%lld",a+i);
	} sort(a+1,a+(n<<1));
	b[a[n]]=1; ll cnt=1;
	f[n][1][1]=1;
	for(ll i=n-1;i;i--){
		ll le=(!b[a[n-(n-i)]]), ri=(!b[a[n+(n-i)]]);
		b[a[n-(n-i)]]=b[a[n+(n-i)]]=1;
		for(ll j=1;j<=cnt;j++)
			for(ll k=1;k<=j;k++){
				if(le) add(f[i][j+ri+le-(k-1)][1],f[i+1][j][k]);
				if(ri) add(f[i][k+le+ri][k+le+ri],f[i+1][j][k]);
				add(f[i][j+le+ri][k+le],f[i+1][j][k]);
				for(ll x=1;x<=j;x++)
					if(x<k) add(f[i][x+(j-k+1)+le+ri][x+le],f[i+1][j][k]);
					else if(x>k) add(f[i][k+(j-x+1)+le+ri][k+1+le],f[i+1][j][k]);
			}
		cnt+=le+ri;
	}
	for(ll i=1;i<=cnt;i++)
		for(ll j=1;j<=i;j++) add(ans,f[1][i][j]);
	printf("%lld",ans);
	return 0;
}

AGC041F Histogram Rooks#

思考容斥。

钦定 k 个格子不被覆盖,容斥系数为 (1)k,每个格子所在行和列的并中所有格子都不能放车。

不妨直接考虑钦定了哪些行和列不能放车,设两个集合分别为 Sr,Sc,令 d 为不被这两个集合包含的格子个数。那么贡献是

2dk(1)k×选 k 个格子,恰好覆盖 Sr,Sc 的方案数

后者不好做,考虑二次容斥,钦定一些行和列不被覆盖。

下面称 “二次钦定集合” 为不被二次容斥钦定不被选择的格子覆盖的行和列,“一次钦定集合” 为钦定的不能放车的行和列。

设二次钦定集合为 S2,一次钦定集合为 S1,没有被钦定的格子个数为 d,二次钦定集合中行和列同时包含的格子个数为 t

那么贡献为:

(1)|S1|2dk=0t(1)k(tk)

注意后面的求和式,等价于 [t=0]容斥系数抵消,因此我们只需要求 S2 中行和列两两不交的情况的贡献。

因此我们只需要关心如何求 d,以及是否存在二次钦定。

f[i,j,0/1] 表示对于笛卡尔树上的点 i,其子树管辖的范围中,一共钦定了 j 列,并且不存在/存在二次钦定的列。

转移先合并左右两边,然后枚举 i 管辖的行和列中的钦定情况即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn=410, mod=998244353;
ll n,a[maxn],lc[maxn],rc[maxn],stk[maxn],top,f[maxn][maxn][2],rt,g[maxn][2],pw[maxn*maxn],ans;
ll fac[maxn],ifac[maxn];
ll C(ll n,ll m) {return n<m? 0:fac[n]*ifac[m]%mod*ifac[n-m]%mod;}
void dfs(ll u,ll l,ll r,ll ff){
	if(lc[u]) dfs(lc[u],l,u-1,u);
	if(rc[u]) dfs(rc[u],u+1,r,u);
	memset(g,0,sizeof g);
	for(ll i=0;i<=u-l;i++)
		for(ll j=0;j<=r-u;j++)
			g[i+j][0]=(g[i+j][0]+f[lc[u]][i][0]*f[rc[u]][j][0])%mod,
			g[i+j][1]=(g[i+j][1]+f[lc[u]][i][1]*f[rc[u]][j][1]
			+f[lc[u]][i][1]*f[rc[u]][j][0]+f[lc[u]][i][0]*f[rc[u]][j][1])%mod;
	for(ll i=r-l;~i;i--){
		g[i+1][0]=(g[i+1][0]+mod-g[i][0])%mod, g[i+1][1]=(g[i+1][1]+g[i][0])%mod;
	} ll d=a[u]-a[ff];
	for(ll i=0;i<=r-l+1;i++){
		for(ll j=0;j<=d;j++){
			f[u][i][1]=(f[u][i][1]+g[i][1]*(j&1? mod-1:1)%mod
			*pw[(r-l+1-i)*(d-j)]%mod*C(d,j))%mod;
			for(ll k=0;j+k<=d;k++){
				f[u][i][0]=(f[u][i][0]+g[i][0]*(j&1? mod-1:1)%mod
				*pw[(r-l+1-i)*(d-j-k)]%mod*C(d,j)%mod*C(d-j,k))%mod;
			}
		}
	}
}
ll power(ll a,ll b=mod-2){
	ll s=1;
	while(b){
		if(b&1) s=s*a%mod;
		a=a*a%mod; b>>=1;
	} return s;
}
int main(){
	f[0][0][0]=1;
	scanf("%lld",&n);
	pw[0]=fac[0]=1;
	for(ll i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
	ifac[n]=power(fac[n]);
	for(ll i=n;i;i--) ifac[i-1]=ifac[i]*i%mod;
	for(ll i=1;i<=n*n;i++) pw[i]=pw[i-1]*2%mod;
	for(ll i=1;i<=n;i++){
		scanf("%lld",a+i);
		while(top&&a[stk[top]]>a[i]) lc[i]=stk[top--];
		if(top) rc[stk[top]]=i;
		stk[++top]=i;
	}
	rt=stk[1];
	dfs(rt,1,n,0);
	for(ll i=0;i<=n;i++)
		ans=(ans+f[rt][i][0]+f[rt][i][1])%mod;
	printf("%lld",ans);
	return 0;
}

出处:https://www.cnblogs.com/Sktn0089/p/18124294

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Lgx_Q  阅读(90)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示