动态规划专项训练记录 2024.3
Paths on the Tree
若使分数最大,则尽量每条路径都到叶子,看到题目说绝对值差不超过1,可以发现是要尽量平均分配,设余r条路径
既然要最大化贡献且剩下的路径要不重复的分配,那就选取前r条从该节点到叶子节点权值和最大的链,递归求取
但有一种情况,若在点u选了路径t,在fa再次选择,就会不满足,所以向上返回时应返回第r+1大的路径
代码:
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
using namespace std;
int n,T,k,head[200005],edgenum;
ll a[200005];
struct edge{
int to,nxt;
}e[400005];
void add_edge(int u,int v)
{
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
head[u]=edgenum;
}
ll ans;
void init()
{
edgenum=ans=0;
memset(head,0,sizeof(head));
}
int son[200005];
void dfs(int u,int fa)
{
son[u]=0;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
son[u]++;
}
}
ll dfs2(int u,int k,int fa)
{
ans+=1ll*k*a[u];
if(!son[u]) return a[u];
int t=k/son[u],r=k%son[u];
priority_queue<ll>q;
while(!q.empty()) q.pop();
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
q.push(dfs2(v,t,u));
}
while(r--)
{
ans+=q.top();
q.pop();
}
return q.top()+a[u];
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
init();
for(int i=2;i<=n;i++)
{
int fa;
scanf("%d",&fa);
add_edge(i,fa);
add_edge(fa,i);
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
dfs(1,0);
//printf("1");
int tmp=dfs2(1,k,0);
printf("%lld\n",ans);
}
return 0;
}
[abc259_f]Select Edges
设
考虑转移,先默认都不连,接着考虑连哪些,很明显,差越大,连上的收益就越大
所以考虑优先队列(大根堆),储存dp[v][1]+e[i].val-dp[v][0]中的正值,dp[u][1]取前d[u]-1项,dp[u][0]取前d[u]项
代码:
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;
int n,d[300005],head[300005],edgenum;
struct edge{
int to,nxt,val;
}e[600005];
void add_edge(int u,int v,int w)
{
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
e[edgenum].val=w;
head[u]=edgenum;
}
ll dp[300005][2];
void dfs(int u,int fa)
{
priority_queue<ll>q;
dp[u][0]=dp[u][1]=0;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
q.push(dp[v][1]+1ll*e[i].val-dp[v][0]);
dp[u][0]+=dp[v][0];
}
if(!d[u])
{
dp[u][1]=-1e18;
return;
}
dp[u][1]=dp[u][0];
int r=0;
while(!q.empty()&&r+1<d[u])
{
if(q.top()>=0)
{
dp[u][1]+=q.top();
}
else break;
q.pop();
r++;
}
dp[u][0]=dp[u][1];
if(d[u]&&!q.empty()&&q.top()>=0) dp[u][0]+=q.top();
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&d[i]);
}
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
}
dfs(1,0);
printf("%lld",dp[1][0]);
return 0;
}
Minimal Coverage
可以发现,若总长为l可以,则总长大于l一定可以,所以可以二分
接着考虑check函数,可以记录可能的终点,设
代码:
#include<cstdio>
using namespace std;
int n,T,a[10004];
bool dp[10005][2005];
bool check(int x)
{
for(int i=0;i<=x;i++)
{
dp[0][i]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=x;j++)
{
dp[i][j]=0;
if(j>=a[i]) dp[i][j]|=dp[i-1][j-a[i]];
if(j+a[i]<=x) dp[i][j]|=dp[i-1][j+a[i]];
}
}
for(int i=0;i<=x;i++)
{
if(dp[n][i]) return 1;
}
return 0;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int l=1,r=2000;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid))
{
r=mid;
}
else
{
l=mid+1;
}
}
printf("%d\n",l);
}
return 0;
}
FTL
设
很明显:
对于
最终答案是
代码:
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll p1,t1,p2,t2,h,s;
ll f[10003],g[10003],ans;
int main()
{
scanf("%lld%lld",&p1,&t1);
scanf("%lld%lld",&p2,&t2);
scanf("%lld%lld",&h,&s);
ans=min(t1*((h-1)/(p1-s)+1),t2*((h-1)/(p2-s)+1));
for(int i=1;i<=h+max(p1,p2);i++)
{
f[i]=g[i]=1e18;
}
for(int i=1;i<=h+max(p1,p2);i++)
{
for(int j=0;j*(p1-s)+(p1+p2-s)<=i;j++)
{
int k;
if(i-j*(p1-s)-(p1+p2-s)!=0)
k=(i-j*(p1-s)-(p1+p2-s)-1)/(p2-s)+1;
else k=0;
f[i]=min(f[i],max(t1*(j+1),t2*(k+1)));
}
for(int j=0;j<i;j++)
{
g[i]=min(g[i],f[i-j]+g[j]);
}
// printf("%d %lld %lld\n",i,f[i],g[i]);
if(i>=h) ans=min(ans,g[i]);
}
printf("%lld",ans);
return 0;
}
Maximum Weight Subset
设
1.选根节点:
2.不选根节点:
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
int n,k,a[205],edgenum,head[205];
struct edge{
int to,nxt;
}e[405];
void add_edge(int u,int v)
{
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
head[u]=edgenum;
}
int f[205][205];
void dfs(int u,int fa)
{
f[u][0]=a[u];
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
}
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
f[u][0]+=f[v][k];
for(int d=1;d<=k;d++)
{
int dis=f[v][d-1];
for(int j=head[u];j;j=e[j].nxt)
{
int w=e[j].to;
if(v==w||w==fa) continue;
dis+=f[w][max(d-1,k-d)];
}
f[u][d]=max(f[u][d],dis);
}
}
for(int i=k;i>0;i--)
{
f[u][i-1]=max(f[u][i-1],f[u][i]);
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
dfs(1,0);
printf("%d",f[1][0]);
return 0;
}
Sonya and Informatics
设0的个数为m,
分三种情况
- 0变少了,则前m个位置的0与后(n-m)个位置的1调换,方案数:
- 0变多了,则前m个位置的1与后(n-m)个位置的0调换,方案数:
- 其余情况,即0的个数不变,方案数
考虑优化,设
代码:
#include<cstdio>//
#include<cstring>//
#define ll long long
using namespace std;
const int p=1e9+7;
int n,m,cnt,sum,t[105];
struct mat{
ll a[105][105];
void clear()
{
memset(a,0,sizeof(a));
}
mat operator * (const mat x)const{
mat ans;
ans.clear();
for(int i=0;i<=sum;i++)
{
for(int j=0;j<=sum;j++)
{
for(int k=0;k<=sum;k++)
{
ans.a[i][j]+=a[i][k]*x.a[k][j]%p;
ans.a[i][j]%=p;
}
}
}
return ans;
}
}Mat;
mat qpowMat(mat x,int y)
{
mat res;
res.clear();
for(int i=0;i<=sum;i++)
{
res.a[i][i]=1;
}
while(y)
{
if(y&1) res=res*x;
x=x*x;
y>>=1;
}
return res;
}
ll qpow(ll x,int y)
{
ll res=1;
while(y)
{
if(y&1) res=res*x%p;
x=x*x%p;
y>>=1;
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&t[i]);
if(t[i]==0) sum++;
}
for(int i=1;i<=sum;i++)
{
if(t[i]==0) cnt++;
}
for(int i=0;i<=sum;i++)
{
int A=1ll*(sum-i)*(sum-i)%p;
int B=1ll*i*(n-2*sum+i)%p;
int C=(1ll*n*(n-1)/2-A-B)%p;
if(i!=sum) Mat.a[i][i+1]=A;
if(i!=0) Mat.a[i][i-1]=B;
Mat.a[i][i]=C;
}
Mat=qpowMat(Mat,m);
printf("%lld",qpow(qpow(n*(n-1)/2,m),p-2)*Mat.a[cnt][sum]%p);
return 0;
}
Square Subsets
因为完全平方数的因子均有偶数个,而70以内只有19个质数,考虑状压,设目前是第i个数,每个质因子的奇偶状态为j,方案数直接由dp[i-1][j^state[i]]和dp[i-1][j]即可
但n很大,
代码:
#include<cstdio>
#define ll long long
using namespace std;
const int p=1e9+7;
int pr[25]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79};
int n,a[100005],t[75],state[75];
int dp[72][524290],fac[100005],inv[100005];
int qpow(int x,int y)
{
int res=1;
while(y)
{
if(y&1) res=1ll*res*x%p;
x=1ll*x*x%p;
y>>=1;
}
return res;
}
ll C(int m,int n)
{
if(m>n) return 0;
return 1ll*fac[n]*inv[m]%p*inv[n-m]%p;
}
int main()
{
scanf("%d",&n);
fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=1ll*fac[i-1]*i%p;
}
inv[n]=qpow(fac[n],p-2);
for(int i=n-1;i>=0;i--)
{
inv[i]=1ll*inv[i+1]*(i+1)%p;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
t[a[i]]++;
if(state[a[i]]) continue;
for(int j=0;j<19;j++)
{
int cnt=0;
int v=a[i];
while(v%pr[j]==0)
{
v/=pr[j];
cnt++;
}
state[a[i]]|=((cnt%2)<<j);
}
}
dp[0][0]=1;
for(int i=0;i<70;i++)
{
ll a=0,b=0;
for(int j=0;j<=t[i+1];j++)
{
if(j%2) b=(b+C(j,t[i+1]))%p;
else a=(a+C(j,t[i+1]))%p;
}
for(int j=0;j<(1<<19);j++)
{
dp[i+1][j]=(1ll*dp[i+1][j]+1ll*dp[i][j]*a%p)%p;
dp[i+1][j^state[i+1]]=(1ll*dp[i+1][j^state[i+1]]+1ll*dp[i][j]*b%p)%p;
}
}
printf("%d",(dp[70][0]-1+p)%p);
return 0;
}
Pillars
设
用值域线段树优化即可
代码:
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
int n,rt,ans,ed;
ll a[100005],d,p=1e15;
int dp[100005];
int ls[6400005],rs[6400005],sum[6400005],cnt;
int update(int id,ll l,ll r,ll x,int c)
{
if(l>x||x>r) return id;
if(!id) id=++cnt;
if(l==r)
{
sum[id]=max(sum[id],c);
return id;
}
ll mid=(l+r)>>1;
ls[id]=update(ls[id],l,mid,x,c);
rs[id]=update(rs[id],mid+1,r,x,c);
sum[id]=max(sum[ls[id]],sum[rs[id]]);
return id;
}
int query(int id,ll l,ll r,ll x,ll y)
{
if(l>y||x>r||x>y) return 0;
if(!id) return 0;
if(x<=l&&y>=r)
{
return sum[id];
}
ll mid=(l+r)>>1;
return max(query(ls[id],l,mid,x,y),query(rs[id],mid+1,r,x,y));
}
int ans1[100005];
int main()
{
scanf("%d%lld",&n,&d);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++)
{
int x=query(rt,1,p,1,a[i]-d);
int y=query(rt,1,p,a[i]+d,p);
dp[i]=max(x,y)+1;
//printf("%d %d %d\n",x,y,dp[i]);
if(dp[i]>ans)
{
ans=dp[i];
ed=i;
}
rt=update(rt,1,p,a[i],dp[i]);
}
printf("%d\n",ans);
int now=ed,x=1;
ans1[1]=ed;
for(int i=ed;i>0;i--)
{
if(abs(a[now]-a[i])>=d&&dp[i]+1==dp[now])
{
now=i;
ans1[++x]=i;
}
}
for(int i=x;i>0;i--)
{
printf("%d ",ans1[i]);
}
return 0;
}
Magic Gems
题意是求一个01串s,0的个数记为p,1的个数记为q,使
设
时间复杂度
代码:
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int p=1e9+7;
ll n;
int m;
struct mat{
ll a[105][105];
void clear()
{
memset(a,0,sizeof(a));
}
mat operator * (const mat &x)const{
mat ans;
ans.clear();
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=1;k<=m;k++)
{
ans.a[i][j]+=a[i][k]*x.a[k][j];
ans.a[i][j]%=p;
}
}
}
return ans;
}
}t;
mat qpow(mat x,ll y)
{
mat res;
res.clear();
for(int i=1;i<=m;i++) res.a[i][i]=1;
while(y)
{
if(y&1) res=res*x;
x=x*x;
y>>=1;
}
return res;
}
int main()
{
scanf("%lld%d",&n,&m);
t.a[1][1]=t.a[1][m]=1;
for(int i=2;i<=m;i++)
{
t.a[i][i-1]=1;
}
mat ans=qpow(t,n);
printf("%d",ans.a[1][1]);
return 0;
}
Steps to One
不会莫比乌斯反演,写个分解质因数的解吧
设
若
考虑优化,可以用容斥原理记录与i的最大公约数为j的数的个数,直接求解即可
代码:
#include<cstdio>
#include<vector>
#define ll long long
using namespace std;
const int p=1e9+7;
int m;
ll f[100005];
ll qpow(ll x,int y)
{
ll res=1;
while(y)
{
if(y&1) res=res*x%p;
x=x*x%p;
y>>=1;
}
return res;
}
vector<int>v[100005],dp[100005];
int main()
{
scanf("%d",&m);
f[1]=0;
for(int i=m;i>0;i--)
{
for(int j=i;j<=m;j+=i)
{
v[j].push_back(i);
dp[j].push_back(0);
}
}
f[1]=1;
for(int i=2;i<=m;i++)
{
for(int j=0;j<v[i].size();j++)
{
dp[i][j]+=m/v[i][j];
for(int k=j+1;k<v[i].size();k++)
{
if(v[i][j]%v[i][k]==0) dp[i][k]-=dp[i][j];
}
}
f[i]=qpow(m-dp[i][0],p-2)*m%p;
for(int j=1;j<v[i].size();j++)
{
f[i]=(f[i]+f[v[i][j]]*dp[i][j]%p*qpow(m-dp[i][0],p-2)%p)%p;
}
}
ll ans=0;
for(int i=1;i<=m;i++) ans=(ans+f[i])%p;
printf("%lld",ans*qpow(m,p-2)%p);
return 0;
}
Jellyfish and EVA
设
考虑如何求
设
显然,
考虑转移,若转移到的点不是
- 若j<k,那么j从当前的第j优节点转化为第j-1优节点,有
- 若j>k,那么j从当前的第j优节点转化为第j-2优节点,有
所以方程为:
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=5000;
int n,m,d[5005],T,head[5005],edgenum;
double g[5005][5005],p[5005],b[5005];
bool cmp(double a,double b)
{
return a>b;
}
struct edge{
int to,nxt;
}e[200005];
void add_edge(int u,int v)
{
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
head[u]=edgenum;
}
bool vis[5005];
void init()
{
memset(head,0,sizeof(head));
memset(d,0,sizeof(d));
memset(vis,0,sizeof(vis));
edgenum=0;
}
void dfs(int u)
{
if(vis[u]) return;
double a[5005];
int id=0;
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
dfs(v);
a[++id]=p[v];
}
sort(a+1,a+id+1);
p[u]=0.0;
for(int i=1;i<=id;i++)
{
p[u]+=g[id][i]*a[i];
}
if(u==n) p[u]=1.0;
}
int main()
{
g[1][1]=1,g[2][2]=0.5;
for(int i=3;i<=N;i++)
{
b[1]=g[i][1]=1.0/(i*1.0);
for(int j=2;j<=i;j++)
{
g[i][j]=g[i-2][j-2]*(j-2)/(i*1.0)+g[i-2][j-1]*(i-j)/(i*1.0);
b[j]=g[i][j];
}
sort(b+1,b+i+1);
for(int j=1;j<=i;j++) g[i][j]=b[j];
}
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
}
dfs(1);
printf("%.12lf\n",p[1]);
}
return 0;
}
Transitive Graph
可以发现,因为会重复连边的过程,若原图a可以到达b,连边后a,b可直达,所以在一个强连通分量中,连边后是完全图
所以,在强连通分量中,可以从任意点进,任意点出,且不重不漏走完所有点
所以可以先缩点,再在DAG上跑DP
代码:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long
using namespace std;
int n,m,T,a[200005],head[200005],edgenum,x[200005],y[200005];
struct edge{
int to,nxt;
}e[200005];
void add_edge(int u,int v)
{
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
head[u]=edgenum;
}
int dfn[200005],low[200005],idx,belong[200005];
int scc,top,s[200005];
bool ins[200005];
void tarjan(int i)
{
dfn[i]=low[i]=++idx;
s[++top]=i;
ins[i]=1;
for(int u=head[i];u;u=e[u].nxt)
{
int j=e[u].to;
if(!dfn[j])
{
tarjan(j);
low[i]=min(low[i],low[j]);
}
else if(ins[j])
{
low[i]=min(low[i],dfn[j]);
}
}
int v=0;
if(low[i]==dfn[i])
{
scc++;
while(v!=i)
{
v=s[top];
top--;
belong[v]=scc;
ins[v]=0;
}
}
}
int d[200005],dis[200005],size[200005];
ll val[200005],sccval[200005],ans_dis,ans_val;
queue<int>q;
void init(int x)
{
for(int i=1;i<=x;i++)
{
dis[i]=size[i]=val[i]=sccval[i]=0;
d[i]=head[i]=0;
belong[i]=dfn[i]=low[i]=0;
}
scc=top=edgenum=idx=ans_dis=ans_val=0;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
init(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x[i],&y[i]);
if(x[i]!=y[i]) add_edge(x[i],y[i]);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i]) tarjan(i);
size[belong[i]]++;
sccval[belong[i]]=sccval[belong[i]]+a[i];
}
for(int i=1;i<=n;i++) head[i]=0;
edgenum=0;
for(int i=1;i<=m;i++)
{
int u=belong[x[i]],v=belong[y[i]];
if(u!=v)
{
add_edge(u,v);
d[v]++;
}
}
while(!q.empty()) q.pop();
for(int i=1;i<=scc;i++)
{
if(d[i]==0) q.push(i);
dis[i]=size[i];
val[i]=sccval[i];
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(dis[v]==dis[u]+size[v])
{
val[v]=min(val[v],sccval[v]+val[u]);
}
if(dis[v]<dis[u]+size[v])
{
dis[v]=dis[u]+size[v];
val[v]=sccval[v]+val[u];
}
d[v]--;
if(d[v]==0) q.push(v);
}
}
for(int i=1;i<=scc;i++)
{
if(dis[i]==ans_dis)
{
ans_val=min(val[i],ans_val);
}
if(dis[i]>ans_dis)
{
ans_dis=dis[i];
ans_val=val[i];
}
}
printf("%lld %lld\n",ans_dis,ans_val);
}
return 0;
}
Playoff Fixing
题目不算难懂,它是类似于线段树取min值的过程
假设当前有2n支队伍,则在相邻的两支队伍中必然有一支编号大于n,另一支小于等于n
考虑依次安排淘汰的队伍,设有x个组只确定了1个,k个组确定了0个,则方案数为:
由乘法原理,因为已经被淘汰,所以互干涉,直接相乘即可
代码:
#include<cstdio>
#define ll long long
using namespace std;
const int p=998244353;
int m,a[1000005],vis[1000005],vis2[1000005];
ll fac[1000005],ans=1;
ll qpow(ll x,int y)
{
ll res=1;
while(y)
{
if(y&1) res=res*x%p;
x=x*x%p;
y>>=1;
}
return res;
}
int main()
{
scanf("%d",&m);
int n=1<<m;
for(int i=1;i<=n;i++) a[i]=-1;
for(int i=1;i<=n;i++)
{
int tmp;
scanf("%d",&tmp);
if(tmp!=-1) a[tmp]=i;
}
fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=fac[i-1]*i%p;
}
for(int i=1;i<=m;i++)
{
int cnt=0,tot=0;
for(int j=(1<<(i-1))+1;j<=(1<<i);j++)
{
int tmp=(a[j]-1)>>(m-i+1);
if(a[j]==-1) continue;
if(vis[tmp]==i)
{
printf("0");
return 0;
}
vis[tmp]=i;
cnt++;
}
for(int j=1;j<=(1<<(i-1));j++)
{
int tmp=(a[j]-1)>>(m-i+1);
if(a[j]==-1) continue;
if(vis2[tmp]==i)
{
printf("0");
return 0;
}
vis2[tmp]=vis[tmp]=i;
}
for(int j=0;j<(1<<(i-1));j++)
{
if(vis[j]==i) tot++;
}
ans=ans*fac[(1<<(i-1))-cnt]%p*qpow(2,(1<<(i-1))-tot)%p;
}
printf("%lld",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律