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;
}
posted @ 2026-02-12 10:52  OTn53_qwq  阅读(13)  评论(0)    收藏  举报