2026.2.9 - 2026.2.15 日做题题解
Round 1078 D 题解
设矩阵中 \(1\) 的为 \(sum\),那么最大值肯定为 \(\lfloor \frac{sum}{2} \rfloor\times (sum-\lfloor \frac{sum}{2} \rfloor)\),我们断言我们可以构造出这个答案,事实也是如此。
考虑一列一列的扫过去,如果当前列加上前面的 \(1\) 的个数大于等于 \(\lfloor \frac{sum}{2} \rfloor\),枚举这个列的行进行分段,然后构造即可,具体的可以看图。

其中蓝色的线为我们的构造。
#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
char c=getchar();
int f=1,ans=0;
while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
return ans*f;
}
const int N=3e5+10;
int n,m;
inline void solve(){
n=read(),m=read();
int sum=0;
vector<vector<int>>a(n+1,vector<int>(m+1,0));
for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) a[i][j]=read(),sum+=a[i][j];
printf("%lld\n",sum/2*(sum-sum/2));sum/=2;
if (sum==0){
for (int i=1;i<=n;i++) putchar('D');
for (int i=1;i<=m;i++) putchar('R');
puts("");
return ;
}
for (int j=1;j<=m;j++){
int cnt=0;
for (int i=1;i<=n;i++) cnt+=a[i][j];
if (sum<=cnt){
for (int i=1;i<j;i++) putchar('R');
for (int i=n;i>0;i--){
sum-=a[i][j];
if (sum==0){
for (int k=1;k<i;k++) putchar('D');
putchar('R');
for (int k=1;k<=n-i+1;k++) putchar('D');
for (int k=j+1;k<=m;k++) putchar('R');
putchar(10);
return ;
}
}
}
else sum-=cnt;
}
}
main(){
int T=read();
while(T--) solve();
return 0;
}
Round 1078 E 题解
设 \(f_{i,j}\) 表示从 \((1,1)\) 走到 \((i,j)\) 的最大愉悦度,\(g_{i,j}\) 表示从 \((i,j)\) 走到 \((n,m)\) 的最大愉悦度。
假设我们反转的位置为 \((i,j)\),那么最大的路径分为三部分过 \((i,j)\),从上面穿,从下面穿,具体可以看图。

用前后缀最大值维护一下即可。
#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
char c=getchar();
int f=1,ans=0;
while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
return ans*f;
}
inline void solve(){
int n=read(),m=read();
vector<vector<int>>a(n+2,vector<int>(m+2,0));
vector<vector<int>>f(n+2,vector<int>(m+2,-1e18));
vector<vector<int>>g(n+2,vector<int>(m+2,-1e18));
vector<vector<int>>h1(n+2,vector<int>(m+2,-1e18));
vector<vector<int>>h2(n+2,vector<int>(m+2,-1e18));
for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) a[i][j]=read();
int ans=1e18;
f[1][1]=a[1][1],g[n][m]=a[n][m];
for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (i!=1||j!=1) f[i][j]=max(f[i][j-1],f[i-1][j])+a[i][j];
for (int i=n;i>0;i--) for (int j=m;j>0;j--) if (i!=n||j!=m) g[i][j]=max(g[i][j+1],g[i+1][j])+a[i][j];
for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) h1[i][j]=max(h1[i-1][j],f[i][j]+g[i][j]-a[i][j]);
for (int j=1;j<=m;j++) for (int i=n;i>0;i--) h2[i][j]=max(h2[i+1][j],f[i][j]+g[i][j]-a[i][j]);
for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) ans=min(ans,max(f[i][j]+g[i][j]-3*a[i][j],max(h1[i-1][j+1],h2[i+1][j-1])));
printf("%lld\n",ans);
}
main(){
int T=read();
while(T--) solve();
return 0;
}
AT ABC 444 F
这题神了,参考了部分题解。
一眼二分,考虑判断中位数是否可以 \(\ge mid\),那么相当于要至少有 \(\frac{n+m+1}{2}\) 个木棍的长度 \(\ge mid\)。
显然,我们要对 \(a_i\ge mid\) 的位置反复裁断,假设满足 \(\ge mid\) 的木棍的长度总和为 \(s\),那么最多可以进行 \(\sum_{i=1}^n a_i - n - (s - \frac{n+m+1}{2})\),解一下发现 \(\sum_{i=1}^n a_i - s\ge \frac{n+m-1}{2}\)。
到这里似乎就更有前途了一点,我们只需要最小化 \(s\) 即可。
如果我们暴力拆分的话,复杂度显然不可以接受,考虑使用类似 DP 的方法求解。
对每个 \(a_i\) 单独操作,我们维护一个列表,每一个位置记录两个值:长度和个数,假设他断开分成长度为 \(x,y\) 的木棍,将 \(x,y\) 分开考虑,如果列表的末端的长度跟 \(x\) 相同,直接将个数相加,否则在末尾新加这个元素,\(y\) 同理,最后将这个列表的个数清空,我们只需要列表最后的 \(2\) 个值,因为其他的位置可以继续分割,如果再次选择下面的上式就不成立了。
最后直接按照长度排序,然后贪心判断合法即可。
#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
char c=getchar();
int f=1,ans=0;
while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
return ans*f;
}
const int N=1e5+10;
int n,m,a[N];
map<int,int>mp;
#define pii pair<int,int>
inline bool check(int mid){
mp.clear();
int cnt=0;
for (int i=1;i<=n;i++) if (a[i]>=mid){
cnt++;
vector<pii>tmp;
tmp.push_back({a[i],1});
for (int j=0;j<tmp.size();j++) if (tmp[j].first!=1){
int x=tmp[j].first/2,y=(tmp[j].first+1)/2;
if (y>=mid) if (tmp.back().first==y) tmp.back().second+=tmp[j].second;else tmp.push_back({y,tmp[j].second});
if (x>=mid) if (tmp.back().first==x) tmp.back().second+=tmp[j].second;else tmp.push_back({x,tmp[j].second});
if (x>=mid||y>=mid) tmp[j].second=0;
}
for (int i=(int)tmp.size()-1;i>=max(0ll,(int)tmp.size()-2);i--) mp[tmp[i].first]+=tmp[i].second;
}
if (cnt+m<(n+m+1)/2) return 0;
int ans=0,sum=(n+m+1)/2;
for (int i=1;i<=n;i++) ans+=a[i];
for (auto i:mp){ans-=i.first*min(i.second,sum),sum-=min(i.second,sum);if (sum==0) break;}
return sum==0&&ans>=(n+m-1)/2;
}
inline void solve(){
n=read(),m=read();
for (int i=1;i<=n;i++) a[i]=read();
int l=1,r=1e18,ans=-1;
while(l<=r){
int mid=l+r>>1;
if (check(mid)) ans=mid,l=mid+1;else r=mid-1;
}
printf("%lld\n",ans);
}
main(){
int T=read();
while(T--) solve();
return 0;
}
CF1060E Sergey and Subway
观察样例可以发现,答案即为 \(\sum_{i=1}^n \sum_{j=i}^n \lceil \frac{dis(i,j)}{2} \rceil\),其中 \(dis(x,y)\) 表示 \(x\) 到 \(y\) 的简单路径的长度。
考虑将每个点为根,以根的深度为 \(1\) 计算整个树的深度,那么一个根的答案即为 \(\sum_{i=1}^n \lfloor \frac{dep_i}{2} \rfloor\),最后答案即为全部相加再除以 \(2\)。
考虑使用换根 dp 来做这个事情,由于下取整比较难做,考虑将深度为奇偶拆开来做。
先考虑以 \(1\) 为根,设 \(f_{u,0/1}\) 表示以 \(u\) 为根的子树中,令 \(u\) 的深度为 \(1\),深度为偶数/奇数深度的和,\(g_{u,0/1}\) 表示个数。
转移和换根这部分平凡,做完了。
#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
const int N=2e5+10;
inline int read(){
char c=getchar();
int f=1,ans=0;
while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
return ans*f;
}
vector<int>a[N];
int ans,n,f[N][2],g[N][2];
inline void tfer(int u,int v,int op){f[u][0]+=op*(f[v][1]+g[v][1]),f[u][1]+=op*(f[v][0]+g[v][0]),g[u][0]+=op*g[v][1],g[u][1]+=op*g[v][0];}
void dfs1(int u,int fa){
f[u][1]=g[u][1]=1;
for (auto v:a[u]) if (v^fa) dfs1(v,u),tfer(u,v,1);
}
void dfs2(int u,int fa){
ans+=f[u][0]/2+(f[u][1]-g[u][1])/2;
for (auto v:a[u]) if (v^fa){
int tmpu1=f[u][0],tmpu2=f[u][1],tmpu3=g[u][0],tmpu4=g[u][1];
int tmpv1=f[v][0],tmpv2=f[v][1],tmpv3=g[v][0],tmpv4=g[v][1];
tfer(u,v,-1),tfer(v,u,1);
dfs2(v,u);
f[u][0]=tmpu1,f[u][1]=tmpu2,g[u][0]=tmpu3,g[u][1]=tmpu4;
f[v][0]=tmpv1,f[v][1]=tmpv2,g[v][0]=tmpv3,g[v][1]=tmpv4;
}
}
main(){
n=read();
for (int i=1,u,v;i<n;i++) u=read(),v=read(),a[u].push_back(v),a[v].push_back(u);
dfs1(1,0),dfs2(1,0);
printf("%lld",ans>>1);
return 0;
}

浙公网安备 33010602011771号