【考试总结】2022-01-19
中心城镇问题
不难得到一种复杂度为 的做法,对 数组维护后缀最大值即可
注意转移时如果钦定距离当前点最小距离为 时,当前归并的子树和已经归并的子树中的下标选择都不应该小于
再观察这个做法地冗余在于子树高度和 差很多时很多状态时无意义的,所以使用长链剖分优化即可
实现长链剖分主要是将数组用指针优化,减少继承长儿子时的运算但是要注意选择自己的时候也要加上长儿子上对应的后缀最大值
比较离奇的是每个点的状态不应该开到 而是 ,否则会错
Code Display
const int N=1e6+10;
int n,k,w[N];
vector<int> g[N];
int tmp[N],len[N],son[N];
int dp[N*4],Mx[N*4],*f[N],*sufx[N],*fst=dp,*mxst=Mx;
inline void dfs(int x,int fat){
for(int t:g[x]) if(t^fat){
dfs(t,x);
if(len[t]>len[son[x]]) son[x]=t;
}
len[x]=len[son[x]]+1;
return ;
}
inline int Suf(int x,int pos){
if(pos>len[x]||pos<0) return 0ll;
return sufx[x][pos];
}
inline void Dp(int x,int fat){
if(son[x]){
f[son[x]]=f[x]+1;
sufx[son[x]]=sufx[x]+1;
Dp(son[x],x);
}
f[x][0]=w[x]+Suf(son[x],k-1); sufx[x][0]=max(Suf(x,1),w[x]+Suf(son[x],k-1));
for(int t:g[x]) if(t!=fat&&t!=son[x]){
sufx[t]=mxst; f[t]=fst; fst+=len[t]<<1; mxst+=len[t]<<1;
Dp(t,x);
for(int i=0;i<=len[t]+1;++i){
ckmax(tmp[i],f[x][i]+Suf(t,max(i-1,k-i-1)));
if(i>0) ckmax(tmp[i],f[t][i-1]+Suf(x,max(i,k-i)));
}
for(int i=len[t]+1;i>=0;--i) sufx[x][i]=max(Suf(x,i+1),f[x][i]=tmp[i]),tmp[i]=0;
}
return ;
}
signed main(){
freopen("central.in","r",stdin); freopen("central.out","w",stdout);
n=read(); k=1+read(); rep(i,1,n) w[i]=read();
for(int i=1;i<n;++i){
int u=read(),v=read();
g[u].pb(v); g[v].pb(u);
}
dfs(1,0);
sufx[1]=mxst; f[1]=fst;
mxst+=len[1]<<1; fst+=len[1]<<1;
Dp(1,0);
int ans=0;
for(int i=0;i<=len[1];++i) ckmax(ans,f[1][i]);
print(ans);
return 0;
}
心理阴影
容易找到的 的做法是在两个点的 处计算两个点逆序的概率
这时概率可以使用逆序方案除以总方案数来算,而相对顺序的总合并方案是
设两个点 在 处的两向儿子分别为 ,可以计算出 在 的子树里面排位为 的概率, 也要做这样的工作
那么枚举两个点在原排列中的排位,新序列中的排位,在新序列中两个点排位之间的两个原排列中数字的数量再组合数即可
满分做法非常巧妙,我们计算 表示 两个点的子树合并后逆序对的平均数,枚举整个序列是 在首位还是 在首位
直接的贡献是 表示在 的子树中小于等于 的数的数量,剩下的问题是原问题的子问题,递归求解即可
而 在首位的概率是 ,计算方法是钦定 在首位时剩下点分布的组合数比总方案数
Code Display
const int N=5010;
int ans,rt,n,fa[N],siz[N],inv[N];
vector<int> g[N],sub[N];
int leq[N][N];
inline void dfs(int x,int fat){
fa[x]=fat; sub[x].pb(x);
siz[x]=1; rep(i,x,n) leq[x][i]++;
for(int i:g[x]) if(i!=fat){
dfs(i,x),siz[x]+=siz[i];
rep(j,1,n) leq[x][j]+=leq[i][j];
for(auto t:sub[i]) sub[x].pb(t),ans+=x>t;
}
return ;
}
int f[N][N];
inline int calc_f(int u,int v){
if(~f[v][u]) return f[v][u];
if(~f[u][v]) return f[u][v]; f[u][v]=add(mul(siz[u],leq[v][u]),mul(siz[v],leq[u][v]));
for(auto t:g[u]) if(t^fa[u]){
//u in the front
ckadd(f[u][v],mul(siz[u],calc_f(t,v)));
}
for(auto t:g[v]) if(t^fa[v]){
//v in the front
ckadd(f[u][v],mul(siz[v],calc_f(t,u)));
}
return f[u][v]=mul(f[u][v],inv[siz[u]+siz[v]]);
}
inline void get_ans(int x){
for(auto t:g[x]) if(t!=fa[x]) get_ans(t);
int siz=g[x].size();
rep(i,0,siz-1) if(g[x][i]^fa[x]) rep(j,i+1,siz-1) if(g[x][j]^fa[x]) ckadd(ans,calc_f(g[x][i],g[x][j]));
return ;
}
signed main(){
freopen("nightmare.in","r",stdin);
freopen("nightmare.out","w",stdout);
n=read(); rt=read();
inv[0]=inv[1]=1;
for(int i=2;i<=n;++i) inv[i]=mod-mul(mod/i,inv[mod%i])%mod;
rep(i,1,n-1){
int u=read(),v=read();
g[u].pb(v); g[v].pb(u);
} dfs(rt,0);
memset(f,-1,sizeof(f));
get_ans(rt);
print(ans);
return 0;
}
pajel 游戏
主要思想是可以使用“右手扶墙”这一做法来将每个四联通的连通块周围所有的有颜色的格子取出
要减少连通块,那么这些有颜色的格子中排位相邻但是在原图中不相邻的格子对要连起来
这时候这个连通块剩下的空格全填一个颜色即可,填入哪个颜色是等价的
由于实现代码造成的正确性并不高,所以我完整手玩了 10 个测试点,重复玩了 6,10 个测试点
手玩的思想也是完整执行右手扶墙
感谢 401rk8 和 zero4338 的帮助,手玩了一整天,本来要写一份工作报告的,但是提交文件没了就不写了
Code Display
Display 呢?提答题要什么Code
还是完全人脑图灵机的提答题
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律