2022 CCPC 桂林
B
题面看着很吓人,但只要读完就发现很好理解,并且根据题意暴力状压DP即可。
原本忘记可以调顺序,发现后纠结了一下,注意到重复选必然更劣故不用管,所以状压转移的时候,直接枚举选哪个就可以了,经过预处理后效率为
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=405,M=20;
void Max(int& x,int y){
if(y>x) x=y;
}
void Min(int& x,int y){
if(y<x) x=y;
}
int n,m,pw[M];
struct node{
int op,tim,mem;
}p[M],tmp[N][M];
void init1(){
int idx[N];
idx['O']=0; idx['W']=1; idx['T']=2; idx['M']=3; idx['R']=4;
for(int i=0;i<M;i++) pw[i]=(1<<i);
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
char c1,c2;
scanf(" %c%c,%d/%d",&c1,&c2,&tmp[i][j].tim,&tmp[i][j].mem);
tmp[i][j].op=idx[(int)c1];
}
}
for(int j=1;j<=m;j++){
for(int i=1;i<=n;i++){
node u=tmp[i][j];
Max(p[j].tim,u.tim);
Max(p[j].mem,u.mem);
if(!p[j].op && u.op){
p[j].op=u.op;
break;
}
}
}
//for(int j=1;j<=m;j++) cout<<j<<" "<<p[j].op<<" "<<p[j].tim<<" "<<p[j].mem<<endl;
}
int a[N],b[N],lim[1<<18];
void init2(){
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++){
node u=tmp[i][j+1],v=p[j+1];
if(u.op && u.op==v.op) a[i]+=pw[j*3];
if(u.tim==v.tim) a[i]+=pw[j*3+1];
if(u.mem==v.mem) a[i]+=pw[j*3+2];
if(u.op && u.op!=v.op) b[i]+=pw[j*3];
if(u.tim>v.tim) b[i]+=pw[j*3+1];
if(u.mem>v.mem) b[i]+=pw[j*3+2];
}
//cout<<i<<" "<<a[i]<<" "<<b[i]<<endl;
}
for(int i=0;i<pw[m*3];i++){
int u=0;
for(int j=0;j<m;j++){
if( !(i&pw[j*3]) ) u+=pw[j*3];
}
lim[i]=u*7;
}
}
int f[1<<18],frt[1<<18],pos[1<<18];
void dp(){
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int j=0;j<pw[m*3];j++){
for(int i=1;i<=n;i++){
if(b[i]&lim[j]) continue;
int u=(j|(a[i]&lim[j]));
if(f[j]+1<f[u]){
f[u]=f[j]+1;
frt[u]=j;
pos[u]=i;
}
}
}
int s=pw[m*3]-1;
for(int j=0;j<m;j++) if(!p[j+1].op) s-=pw[j*3];
//cout<<"s="<<s<<endl;
cout<<f[s]<<endl;
stack<int>q;
while(s){
q.push(pos[s]);
s=frt[s];
}
while(!q.empty()) printf("%d ",q.top()),q.pop();
}
int main()
{
//freopen("1.in","r",stdin);
init1();
init2();
dp();
return 0;
}
D
列出式子后直接往普通幂转下降幂的方向想(因为变成下降幂之后就可以无穷级数求和再求导了),然后搞了半天发现不可做(中间推错了几次,一度以为可做),因为最后变成,用各种第二类斯特林数的公式都难以高效计算出所有的。
所以考虑其它方向:
首先发现中的n拆成两部分后,用二项式展开是EGF的卷积形式,所以求出一张牌的期望F后,即为n张牌的期望。
列出级数的式子后发现是等比*k次等差,考虑经典错位相减,然后发现F(k)可以化成i<k的F(i)乘组合数,用EGF即可求出F。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<20)+5,P=998244353,G[2]={3,(P+1)/3};
int Mul(int a,int b){
return 1ll*a*b%P;
}
int Add(int a,int b){
a+=b;
if(a>=P) a-=P;
if(a<0) a+=P;
return a;
}
inline int fpw(int a,int x){
int s=1;
for(;x;x>>=1,a=1ll*a*a%P) if(x&1) s=1ll*s*a%P;
return s;
}
int rv[N],gp[2][N],iv[N],fc[N],fv[N];
void init(int n){
fc[0]=1;
for(int i=1;i<n;i++) iv[i]=fpw(i,P-2),fc[i]=1ll*fc[i-1]*i%P;
fv[n-1]=fpw(fc[n-1],P-2);
for(int i=n-2;~i;i--) fv[i]=1ll*fv[i+1]*(i+1)%P;
for(int p=0;p<2;p++){
for(int i=1;i<n;i<<=1){
gp[p][i]=1;
int t=fpw(G[p],(P-1)/(i<<1));
for(int j=i+1;j<(i<<1);j++) gp[p][j]=1ll*gp[p][j-1]*t%P;
}
}
}
void print(char name[],int n,int* a){
printf("%s n=%d\n",name,n);
for(int i=0;i<n;i++) cout<<a[i]<<" "; puts("");
}
inline void dft(int* a,int n,int p){
for(int i=0;i<n;i++) if(i<rv[i]) swap(a[i],a[rv[i]]);
for(int i=1;i<n;i<<=1){
for(int j=0;j<n;j+=(i<<1)){
for(int k=0;k<i;k++){
int &A=a[i+j+k],&B=a[j+k],t=1ll*gp[p][i+k]*A%P;
A=B-t; if(A<0) A+=P;
B=B+t; if(B>=P) B-=P;
}
}
}
if(p) for(int i=0;i<n;i++) a[i]=1ll*a[i]*iv[n]%P;
}
inline int Rev(int m){
int p=0,n=1;
while(n<m) n<<=1,p++;
for(int i=0;i<n;i++) rv[i]=(rv[i>>1]>>1)|((i&1)<<(p-1));
return n;
}
inline void poly_mul(int m,int* A,int* B,int* C){
int n=Rev(m);
dft(A,n,0); dft(B,n,0);
for(int i=0;i<n;i++) C[i]=1ll*A[i]*B[i]%P;
dft(C,n,1);
fill(C+m,C+n,0);
}
//C=A*B m:需要C的0~(m-1)项,m~(n-1)为空
int C[N],D[N];
inline void poly_inv(int m,int *a, int *b) {
if(m==1){
b[0]=fpw(a[0],P-2);
return;
}
poly_inv((m + 1)>>1,a,b);
int n=Rev(m<<1);
copy(a,a+m,C);
fill(C+m,C+n,0);
dft(b,n,0); dft(C,n,0);
for(int i=0;i<n;i++) b[i]=1ll*b[i]*(P+2-1ll*b[i]*C[i]%P)%P;
dft(b,n,1);
fill(b+m,b+n,0);
}
//a*b=1,给定a的0~(m-1)项,返回b的0~(m-1)项
//需要保证初始b为空!
inline void poly_Ln(int m,int* a,int* b){
poly_inv(m,a,b);
for(int i=0;i<m-1;i++) C[i]=1ll*a[i+1]*(i+1)%P; C[m-1]=0;
int n=Rev(m<<1);
fill(C+m,C+n,0);
dft(C,n,0); dft(b,n,0);
for(int i=0;i<n;i++) b[i]=1ll*C[i]*b[i]%P;
dft(b,n,1);
for(int i=m-1;i;i--) b[i]=1ll*b[i-1]*iv[i]%P; b[0]=0;
fill(b+m,b+n,0);
}
//b=Ln(a),给定a的0~(m-1)项,返回b的0~(m-1)项
//需要保证初始b为空!
inline void poly_Exp(int m,int* a,int* b){
if(m==1){
b[0]=1;
return;
}
poly_Exp((m+1)>>1,a,b);
poly_Ln(m,b,D);
for(int i=0;i<m;i++) D[i]=(P+a[i]-D[i]+(i==0))%P;
int n=Rev(m<<1);
dft(b,n,0); dft(D,n,0);
for(int i=0;i<n;i++) b[i]=1ll*b[i]*D[i]%P;
dft(b,n,1);
fill(b+m,b+n,0);
fill(D,D+n,0);
}
//b=Exp(a),给定a的0~(m-1)项,返回b的0~(m-1)项
//需要保证初始b为空!
int n,m,a,b,p,q;
int A[N],B[N],E[N],F[N],g[N];
int main(){
cin>>n>>m>>a>>b;
init((n+m)<<2);
p=Mul(a,fpw(b,P-2));
q=Add(P+1,-p);
B[0]=1;
for(int i=0;i<=m;i++) A[i]=fv[i],B[i]=Add(B[i],P-Mul(q,A[i]));
poly_inv(m+1,B,E);
poly_mul(m+m+1,A,E,F);
fill(F+m+1,F+m+m+1,0);
poly_Ln(m+1,F,g);
fill(F,F+m+1,0);
for(int i=0;i<=m;i++) g[i]=Mul(g[i],n);
poly_Exp(m+1,g,F);
for(int i=0;i<=m;i++) printf("%d\n",Mul(F[i],fc[i]));
return 0;
}
E
回想了一下场上卡的地方,真的蠢哭了。。。大概是直接用坐标表示两个向量,算出叉积,转化成的最小值,麻烦的是这个c(1e18范围),要尽量往0靠,然后就要在特解的基础上找到绝对值最小的解,但分析一下,发现这样的解可能是超过1e18的(1e18*1e9),于是往各种奇怪的方向搞了半天,还是自闭了,最后队友换了个做法才过。
但凡稍微冷静一下,就发现可以令其中一个为原点,就变成一个已知向量和一个未知向量叉积的最小非0解,直接exgcd找任意一组即可!!
复盘了一下当时的心态,就觉得是个签到题,没分析原来做法的不合理性(在写的时候其实有感到一点诡异,这时候就应该回过头想清楚一点),也不想麻烦队友,wa了几遍之后心态很崩,花了很多时间才注意到答案会超;然后也没回头想,就直接瞎调整,写了一堆很复杂的东西还不对(甚至在怀疑自己对exgcd有什么误解)。。。吸取的教训就是要保持冷静和多交流!!
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void exgcd(ll a,ll b,ll& x,ll& y){
if(!b){
x=1;
y=0;
return;
}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
void work(){
ll a,b,c,d,x,y;
cin>>a>>b>>c>>d;
c=a-c;
d=b-d;
if(!c){
cout<<a+1<<" "<<0<<endl;
return;
}
if(!d){
cout<<0<<" "<<b+1<<endl;
return;
}
exgcd(-d,c,x,y);
cout<<a+x<<" "<<b+y<<endl;
}
int main()
{
int T; cin>>T; while(T--) work();
return 0;
}
G
场上开到这题的时候已经很急了,没多想性质就直接写了个即子树内两条链状态的方法(大概是题解最后提到的很麻烦状压DP),然后一直没过。。。
原本以为分讨很麻烦,但其实不会:先固定一条链,然后考虑另一条链的LCA;如果两条链相交且LCA在固定的链的链所在的子树外,那么会在固定另一条链时记到;否则LCA只会在固定的链上,而对于这种情况,可以通过改为交于一点,使答案更优。于是分别考虑交于一点和两条链不相交的情况,换根DP即可。
感觉越是在紧张的状态下,就越会对需要观察性质、思考结论这样的思维活动失去自信,从而转向无脑暴力做法,而在大多数情况下只会使情况更复杂(其实不对劲的话也能大概感觉到);在各种情况下想题,都实事求是地考虑题目的方向和性质(而不是急于求成,直接套算法或猜不靠谱的结论),这样的心理和思维素质都是需要锻炼的!
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,a[N],hd[N],to[N<<1],nx[N<<1],tt;
void add(int u,int v){
nx[++tt]=hd[u];
to[hd[u]=tt]=v;
}
struct node{
int f,f1,d1,f2,g,d;
}p[N];
bool cmp(node u,node v){
return u.g>v.g;
}
bool cmp2(node u,node v){
return u.f>v.f;
}
void pre(int u,int fa){
vector<node>h;
p[u].d=u;
for(int e=hd[u];e;e=nx[e]) if(to[e]!=fa){
int v=to[e];
pre(v,u);
h.push_back(p[v]);
p[u].g=max(p[u].g,p[v].g);
int x=p[v].f;
if(x>p[u].f2){
if(x>p[u].f1) p[u].f2=p[u].f1,p[u].f1=x,p[u].d1=v;
else p[u].f2=x;
}
}
//cout<<"pre:"<<u<<endl;
int sz=h.size();
//if(!sz) return;
sort(h.begin(),h.end(),cmp);
p[u].g+=a[u];
p[u].f=max(p[u].f1,a[u]+(sz?h[0].g:0)+(sz>1?h[1].g:0));
//cout<<"u="<<u<<" g="<<p[u].g<<" f="<<p[u].f<<endl;
}
int ans;
void dfs(int u,int fa){
vector<node>h;
h.push_back(p[fa]);
for(int e=hd[u];e;e=nx[e]) if(to[e]!=fa){
int v=to[e];
h.push_back(p[v]);
}
int sz=h.size();
sort(h.begin(),h.end(),cmp);
int s=0;
for(int i=0;i<min(4,sz);i++) s+=h[i].g;
ans=max(ans,s);
p[u].g=(sz?h[0].g:0)+a[u];
p[u].f=(sz?h[0].g:0)+(sz>1?h[1].g:0)+a[u];
sort(h.begin(),h.end(),cmp2);
p[u].f1=(sz?h[0].f:0);
p[u].d1=(sz?h[0].d:0);
p[u].f2=(sz>1?h[1].f:0);
for(int e=hd[u];e;e=nx[e]) if(to[e]!=fa){
int v=to[e];
node tu=p[u],tv=p[v];
//new g
if(h[0].d==v){
if(sz>1) p[u].g=h[1].g+a[u];
else p[u].g=a[u];
}
//new fu
int mf=p[u].f1;
if(v==p[u].d1) mf=p[u].f2;
int mg;
if(v==h[0].d) mg=(sz>1?h[1].g:0)+(sz>2?h[2].g:0);
else if(sz>1 && v==h[1].d) mg=h[0].g+(sz>2?h[2].g:0);
else mg=h[0].g+(sz>1?h[1].g:0);
p[u].f=max(mf,mg+a[u]);
ans=max(ans,p[u].f+p[v].f);
dfs(v,u);
p[u]=tu; p[v]=tv;
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int u,v,i=1;i<n;i++) scanf("%d%d",&u,&v),add(u,v),add(v,u);
if(n==1){
puts("0");
return 0;
}
pre(1,0);
//return 0;
dfs(1,0);
cout<<ans<<endl;
return 0;
}
J
对于一些点确定的拓扑图,直观的想法是跑正反两边拓扑序,对每个点求出一个限制较弱的区间;而对于n个区间填数又是典中典;但问题在于这里求出的区间不是充分的,仍然有拓扑关系的限制存在;但分析一下发现,直接贪心做就是满足拓扑序的!因为对于每条边,入点的区间的左右端点,一定分别在出点区间的左右端点之右,那么入点填的数一定大于出点。
思路还是好想的,但合法性需要一些觉悟,或者就大胆一点直接莽也对。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int T,n,m,p[N];
struct edge{
int *hd,*to,*nx,*du,tt;
edge(void){
hd=new int[N];
to=new int[N];
nx=new int[N];
du=new int[N];
}
void add(int u,int v){
nx[++tt]=hd[u];
to[hd[u]=tt]=v;
du[v]++;
}
};
bool is[N];
struct node{
int d,l,r;
}a[N];
struct cmp1{
bool operator () (node u,node v){
return u.l>v.l;
}
};
struct cmp2{
bool operator () (node u,node v){
return u.r>v.r;
}
};
queue<node>q;
void topo(int op,edge E){
//return;
//return;
for(int i=1;i<=n;i++){
if(!p[i] && !E.du[i]) q.push(a[i]);
}
//return;
while(!q.empty()){
node u=q.front();
q.pop();
for(int e=E.hd[u.d];e;e=E.nx[e]){
int v=E.to[e];
E.du[v]--;
if(!op) a[v].l=max(u.l+1,a[v].l);
else a[v].r=min(u.r-1,a[v].r);
if(!E.du[v]) q.push(a[v]);
//t++;
//break;
}
}
}
edge E[2];
void work(int id){
scanf("%d%d",&n,&m);
E[0].tt=E[1].tt=0;
for(int i=1;i<=n;i++){
is[i]=1,a[i]=(node){i,1,n};
for(int op=0;op<2;op++) E[op].hd[i]=E[op].du[i]=0;
}
for(int i=1;i<=n;i++) scanf("%d",&p[i]),is[p[i]]=0;
while(m--){
int u,v;
scanf("%d%d",&u,&v);
if(p[u] && p[v]){
if(p[u]>p[v]){
puts("-1");
while(m--) scanf("%d%d",&u,&v);
return;
}
}
else if(p[u]){
a[v].l=max(a[v].l,p[u]);
}
else if(p[v]){
a[u].r=min(a[u].r,p[v]);
}
else E[0].add(u,v),E[1].add(v,u);
}
//if(T==20000) return;
topo(0,E[0]);
//if(T==20000) return;
topo(1,E[1]);
priority_queue<node,vector<node>,cmp1>q;
priority_queue<node,vector<node>,cmp2>t;
for(int i=1;i<=n;i++){
if(!p[i]) q.push(a[i]);
}
for(int i=1;i<=n;i++){
if(!is[i]) continue;
while(!q.empty()){
node u=q.top();
//cout<<u.l<<" "<<u.r<<" "<<u.d<<endl;
if(u.l>i){
break;
}
q.pop();
t.push(u);
}
if(t.empty()){
puts("-1");
return;
}
node u=t.top();
if(u.r<i){
puts("-1");
return;
}
p[u.d]=i;
t.pop();
}
for(int i=1;i<=n;i++) if(!p[i]){
puts("-1");
return;
}
for(int i=1;i<=n;i++) printf("%d ",p[i]);
puts("");
/*if(T==20000 && id==10000){
int a=1/0;
}*/
}
int main()
{
//srand(time(0));
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
cin>>T; for(int i=1;i<=T;i++) work(i);
return 0;
}
K
分讨+构造题,本质和L差不多,但会更复杂一些。
先考虑一般的情况,大致的思路是尽量构造异或和小的解:
发现只有m是偶数时异或和可以为0,否则最小为1,于是按照m的奇偶分讨;
又发现n为偶数的时候可以分成两组数,相同的部分互相抵消,n为奇数时就两组+剩余一个数;
于是按照n,m的奇偶性分四种情况讨论,以两组尽可能互相抵消的方式,在使得异或和为0或1的同时,每个数不小于1或2,然后可以得到有解的条充分件是一个n,m的不等式,并且感性认知到这也是必要的(不满足就构不出来了)
但在构造上面的解的时候,会发现必须(一组内的数要多余一个),于是需要特判,是显然的,难点在于(感觉这部分比一般的情况更难)
还是按照m的奇偶性分讨,容易想到的构造方式,但对于的两种情况需要特殊构造(原本以为无解,不知道wa哪了卡了很久,感觉如果在考场上会很搞心态,所以对于这种分讨题,还是要多自己构造小数据和特殊数据)
这题虽然思路比较自然,但需要比较细致的分讨和不厌其烦的构造,要场上过还是很有压力的,感觉难度远大于B,甚至略大于D(擅长构造的队友场切了,本来是一大优势,但前面的题击中的盲点过多+一卡题就自闭就浪费了这样的优势)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+5;
int T,lg[N],pw[N],ans[N];
void fill(int t,int s,int r){
for(int i=1;i<t;i++){
ans[i]=s;
if(i<=r) ans[i]++;
ans[i+t-1]=ans[i];
}
}
void work(int id){
int n,m,pd=1;
cin>>n>>m;
int t=n/2,k=m/2,g=lg[k]+1;
if(n==1){
puts("NO");
return;
}
if(n==2){
if(m&1){
if(pw[g]-1==k) puts("NO");
else{
puts("YES");
cout<<k<<" "<<k+1<<endl;
}
}
else{
puts("YES");
cout<<k<<" "<<k<<endl;
}
return;
}
if(n==3){
int a=pw[g-1],b=k-a;
if(m<6) puts("NO");
else if(m&1){
if(!b){
if(k<16) puts("NO");
else{
puts("YES");
cout<<a-1<<" "<<a-5<<" "<<7<<endl;
}
}
else if(b==1){
if(k<17) puts("NO");
else{
puts("YES");
cout<<a-1<<" "<<a-3<<" "<<7<<endl;
}
}
else{
puts("YES");
cout<<a+1<<" "<<b<<" "<<a+b<<endl;
}
}
else{
if(!b && k<8) puts("NO");
else if(!b){
puts("YES");
cout<<a-1<<" "<<a-2<<" "<<3<<endl;
}
else{
puts("YES");
cout<<a<<" "<<b<<" "<<a+b<<endl;
}
}
return;
}
if(m&1){
if(n&1){
if(k<t+t+4) pd=0;
else{
int s=(k-6)/(t-1),r=(k-6)%(t-1);
fill(t,s,r);
ans[n-2]=2; ans[n-1]=4; ans[n]=7;
}
}
else{
if(k<n) pd=0;
else{
int s=(k-2)/(t-1),r=(k-2)%(t-1);
fill(t,s,r);
ans[n-1]=2; ans[n]=3;
}
}
}
else{
if(n&1){
if(k<t+2) pd=0;
else{
int s=(k-3)/(t-1),r=(k-3)%(t-1);
fill(t,s,r);
ans[n-2]=1; ans[n-1]=2; ans[n]=3;
}
}
else{
for(int i=1;i<=n-2;i++) ans[i]=1;
ans[n-1]=ans[n]=(m-n+2)/2;
}
}
if(!pd) puts("NO");
else{
puts("YES");
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
puts("");
}
}
int main()
{
for(int i=2;i<N;i++) lg[i]=lg[i>>1]+1;
pw[0]=1; for(int i=1;i<=30;i++) pw[i]=(pw[i-1]<<1);
cin>>T; for(int i=1;i<=T;i++) work(i);
return 0;
}
L
本质上是一个简单构造,但考场上卡其它题导致思路混乱,没冷静想,非常可惜。。。
先考虑如何判断一个答案是否合法:即对于每个人,它选择概率不为0的下标时,一定是其它人任何情况下的最优解之一。那么发现概率只关心是否为0,并且对于每个人,肯定是有一个下标概率为1,其它为0,这样对自己和别人的合法性都是最优的。
在判断一个选择是否合法时,我们关心的是每个数其它人选择的人数。于是把选择每个下标的人数cnt[i]算出来,问题转化成构造一个大小m的cnt数组,使得对于每一个非0元素,将其-1后,选择该下标是最优解之一。
先考虑没有单独的数的情况,即任意,发现合法,于是就解决了。
在考虑的时候,发现1的个数必须至少三个,又发现只要有3个连续的1,剩下的都放在后面一个,前面补0即可,于是解决了。
剩下的情况,直接特判;时,只剩下或,都单独构造即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=15;
int n,m,c[N];
int main()
{
cin>>n>>m;
if(n==2){
while(n--){
for(int i=1;i<m;i++) cout<<0<<" ";
cout<<1<<endl;
}
return 0;
}
if(n>=m+m){
int t=n/m,r=n%m;
for(int i=1;i<=m;i++) c[i]=t;
for(int i=m-r+1;i<=m;i++) c[i]++;
}
else if(m==2){
c[1]=2;
c[2]=n-2;
}
else if(m==3 && (n==4 || n==5)){
c[2]=c[3]=2;
c[1]=(n==5);
}
else{
if(n>3){
c[m-1]=c[m-2]=c[m-3]=1;
c[m]=n-3;
}
else c[m]=c[m-1]=c[m-2]=1;
}
int p=1;
while(n--){
while(!c[p]) p++;
c[p]--;
for(int i=1;i<=m;i++){
if(i==p) cout<<1<<" ";
else cout<<0<<" ";
}
puts("");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现