首页 > 其他分享 >CF三月D题题解

CF三月D题题解

时间:2023-05-02 18:55:20浏览次数:55  
标签:le int 题解 sum mn CF 三月 mx dis

cf1798d

题意:重排序列,使得其中连续子序列和的绝对值最大的最大值小于序列最大值减最小值,序列和为0

考虑这样一种构造方案:

正负数分类,0直接不管

然后记录当前和sum,当sum非负时,加上一个负数,当sum是负数时,加上一个正数即可

正确性证明:

显然前缀和都是合法的。考虑计算前缀和数组,满足性质:最小的sum小于最小值,最大的sum大于等于最大值。 那么显然合法

无解的情况就是全0

/*
分讨:
1. sum<mx-mn-->mx>0,mn<0-->sum<mx-mn
2. -sum<mx-mn->mx>0,mn<0-->sum>mn-mx
不妨思考一个更强的条件
mn<=sum<=mx
一种合理的构造方式是:
当前sum>0,加一个负数
当前sum<0,加一个正数
当前sum=0,随便加
Why?
由于和是0,则按照这样加sum最大为mx(0+mx),最小为mn(0+mn)
*/
signed main(){
	ios::sync_with_stdio(false);
	cin>>t;
	while(t--){
		int num1=0,tot=0,num2=0,mx=0,mn=0;cnt=0;
		cin>>n;
		for(int i=1;i<=n;i++){
			int x;cin>>x;
			mx=max(mx,x);mn=min(mn,x);
			if(x>0)a[++num1]=x;
			else if(x<0)b[++num2]=x;
			else ans[++tot]=0;
		}
		if(mx==mn){
			cout<<"No\n";continue;
		}
		int sum=0,l=1,r=1;
		while(l<=num1&&r<=num2){
			if(sum<0)sum+=a[l],ans[++tot]=a[l],l++;
			else sum+=b[r],ans[++tot]=b[r],r++;
		}
		while(l<=num1)ans[++tot]=a[l],l++;
		while(r<=num2)ans[++tot]=b[r],r++;
		cout<<"Yes\n";
		for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
		cout<<"\n";
	}
} 

CF1805D

给定一颗树,求 \(ans_{k}(1\le k\le n)\)。

\(ans_k\) 的含义为:在树上将所有 \(dis(u,v)\ge k\) 的点对连边后的连通块个数。

联系到树的直径,设直径长为 \(m\),端点为 \(s,t\)

则 \(ans_k=n(k>m)\)。

引理:

当 \(k\le m\) 时,有且仅有 \(s,t\) 所在的连通块 \(siz>1\)

证明:

\(k=m\) 时,显然成立

现在我们证明了 \(k=a\),要证明 \(k=a-1\)

采用反证法

假设会形成一个 \(siz>1\) 的连通块,不与直径一起。那么设这个连通块的直径为 \(p\),则 \(p\ge a-1\) 对于 \(s,t\) 而言

直径不在这里,所以一定两端点到这个连通块的最大距离 \(\ge p+1\)。Why?假设它不大于,那么这个连通块的直径端点向 \(s,t\) 连边,就会形成一条 \(>dis(s,t)\) 的链,此时出现新的直径,于直径定义不符,所以不成立

引理成立。

所以可以以两个直径端点进行BFS,然后倒序处理删连通块即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
#define N 300400
using namespace std;
int s,t,vis[N],dis[N][3],n,m,head[N],ver[N],nxt[N],tot,ans[N],f[N];
vector<int>cnt[N],cnt1[N];
void add(int u,int v){
	nxt[++tot]=head[u],ver[head[u]=tot]=v;
}
void bfs(int s,int id){
	queue<int>q;
	memset(vis,0,sizeof vis);
	vis[s]=1;
	q.push(s);
	dis[s][id]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=nxt[i]){
			int v=ver[i];
			if(vis[v])continue;
			dis[v][id]=dis[u][id]+1;
			q.push(v);vis[v]=1;
			if(id==0)cnt[dis[v][id]].push_back(v);
			if(id==1)cnt1[dis[v][id]].push_back(v);
		}
	}
}
int find(int x){
	return x==f[x]?x:f[x]=find(f[x]);
}
int main(){
//	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		add(u,v);add(v,u);
	}
	bfs(1,2);s=-1;int mx=-1;
	for(int i=1;i<=n;i++){
		if(dis[i][2]>mx){
			mx=dis[i][2];s=i;
		}
	}
	bfs(s,1);
	mx=-1;
	for(int i=1;i<=n;i++){
		if(dis[i][1]>mx){
			mx=dis[i][1];t=i;
		}
	}
	bfs(t,0);int sum=n;
	for(int i=1;i<=n;i++)f[i]=i;
	//cout<<s<<" "<<t<<"\n";
	for(int i=mx;i;--i){
		for(int j=0;j<cnt1[i].size();j++){
			int u=cnt1[i][j];u=find(u);
			if(u!=find(s)){
				sum--;
				f[u]=find(s);
			}
		}
		for(int j=0;j<cnt[i].size();j++){
			int u=cnt[i][j];u=find(u);
			if(u==find(t))continue;
			f[u]=find(t);
			sum--;
		}
		ans[i]=sum; 
	}
	for(int i=1;i<=n;i++){
		if(i>mx)cout<<n<<" ";
		else cout<<ans[i]<<" ";
	}
	return 0;
}

CF1806C

首先,让我们来手推一下。

我们已知的条件是:

\[\left\{ \begin{aligned} x_1x_2x_3…x_n&=x_{n+1}+x_{n+2}+x_{n+3}+…+x_{2n}\\ x_2x_3x_4…x_{n+1}&=x_1+x_{n+2}+x_{n+3}+…+x_{2n}\\ x_3…x_nx_{n+1}x_{n+2}&=x_1+x_{n+2}+x_{n+3}+…+x_{2n}\\ … \end{aligned} \right. \]

以前两个式子为例,有:

\(x_2x_3…x_n(x_1-x_n)=x_n-x_1\)

这个方程有两种可能:

  1. \(x_2x_3……x_n=-1\implies x_i=1\)或\(-1,i\in[2,n]\)
  2. \(x_1-x_n=0\implies x_1=x_n\)

由于式子一共有 \(2n\choose n\) 个,可以推出任意的关系。

进而对于第一种情况,我们可以确定出当 \(n\) 为偶数的时候,好的数组是 \((-1,-1,-1,n,-1,-1…)\),以及它的全排列 (只有一个 \(n\) )。

对于第二种情况,所有值都相等,则有: \(x^n=xn\implies x_1=0,x_2=\sqrt[n-1]{n}\)

显然,对于 \(x_2\),当且仅当 \(n=2\) 时为整数。

需要注意,当 \(n=1\) 的时候,好的数组是 \((x,x)(x\in \mathbb{Z})\)

综上所述,有:

  1. 对于 \(n=1\) ,这个最优的 \(q\) 为 \((p_1,p_1),(p_2,p_2)\)两种可能,距离都是 \(|p_1-p_2|\)
  2. 对于 \(n=2\),好的数组有且仅有:\((0,0,0,0),(2,2,2,2),(-1,-1,2,-1)\) 及其排列
  3. 对于 \(n\) 为偶数且 \(n>2\),好的数组有且仅有 \((0,0,0,0…,0),(-1,-1,n,-1,…,-1)\) 及其排列
  4. 对于 \(n\) 为奇数且 \(n>2\),好的数组有且仅有 \((0,0,0,0,……,0)\)

对于 \((-1,-1,n,-1,…,-1)\) 及其排列的情况,即为 \(\sum_{i=1}^{2n}|p_i+1|-\min_{1\le i\le 2n}\lbrace |p_i-n|-|p_i+1|\rbrace\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[2060606],n,T;
signed main(){
	ios::sync_with_stdio(false);
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n<<1;i++)cin>>a[i];
		if(n==1){
			cout<<abs(a[1]-a[2])<<"\n";
		}
		else if(n==2){
			int ans1=0;
			for(int i=1;i<=n<<1;i++)ans1+=abs(a[i]-2);
			int ans2=0;
			for(int i=1;i<=n<<1;i++)ans2+=abs(a[i]);
			int ans3=0;
			for(int i=1;i<=n<<1;i++)ans3+=abs(a[i]+1);
			int mx=0x3f3f3f3f3f3f;
			for(int i=1;i<=n<<1;i++)mx=min(mx,abs(a[i]-n)-abs(a[i]+1));
			ans3+=mx;
			ans2=min(ans2,ans3);
			cout<<min(ans1,ans2)<<"\n";
		}
		else if((n&1)==0){
			int ans2=0;
			for(int i=1;i<=n<<1;i++)ans2+=abs(a[i]);
			int ans3=0;
			for(int i=1;i<=n<<1;i++)ans3+=abs(a[i]+1);
			int mx=0x3f3f3f3f3f3f;
			for(int i=1;i<=n<<1;i++)mx=min(mx,abs(a[i]-n)-abs(a[i]+1));
			ans3+=mx;
			cout<<min(ans2,ans3)<<"\n";
		}
		else {
			int ans2=0;
			for(int i=1;i<=n<<1;i++)ans2+=abs(a[i]);
			cout<<ans2<<"\n";
		}
	}
} 

CF1810D

不等式缩放即可

一组 \(a,b,n\) 可以确定长度 \(h\) 为 \((a-b)(n-1)+1\le h\le (a-b)(n-1)+a\)

相同的,询问时仅需要判断在这个范围内唯一确定即可

设询问 \(a,b\),则设当前不等式为 \(mn\le h\le mx\)

则最大的 \(n\) 为 \(\left\lceil\frac{(mx-b)}{a-b}\right\rceil\)。

然后就只需要判断这个 \(n\) 可以确定的最小值与 \(mn\) 的关系即可

可能越界的就是 \((n-2)(a-b)+a\ge mn\) 这样就没了

注意特判 \(n=1\)

signed main(){
	cin>>t;
	while(t--){
		int q;cin>>q;
		int mn=0,mx=1e18,num=0;
		while(q--){
			int opt,a,b,n;
			cin>>opt>>a>>b;
			if(opt==1){
				cin>>n;
				int new_mn,new_mx;
				if(n>1)new_mn=a*(n-1)-b*(n-2)+1;
				else new_mn=0;
				new_mx=a*n-b*(n-1);
				if(new_mx>=mn&&new_mn<=mx){
					mx=min(mx,new_mx);
					mn=max(new_mn,mn);
					f[++num]=1;continue;
				}
				f[++num]=0;
			} 
			else {
				n=(mx-b+a-b-1)/(a-b);n=max(n,1ll);
				if((n-1)*a-(n-2)*b>=mn&&n!=1)f[++num]=-1;
				else f[++num]=n;
			}
		}
		for(int i=1;i<=num;i++)cout<<f[i]<<" ";
		cout<<"\n";
	}
} 

标签:le,int,题解,sum,mn,CF,三月,mx,dis
From: https://www.cnblogs.com/oierpyt/p/17368045.html

相关文章

  • 「BZOJ2144」跳跳棋-题解
    「BZOJ2144」跳跳棋个人评价挺好的一道题,难点在于想到树这个结构和建树1题面跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置。我们要通过最少的跳动把他们的位置移......
  • Educational Codeforces Round 147 (Rated for Div. 2) 题解
    ALink。模拟,代码。BLink。模拟,代码。CLink。我们设\(c\)为最后相同的字符。性质:我们一定不会删除字符\(c\)。因此以\(c\)为最后字符的操作次数就是不包含字符\(c\)的极大段的最小操作次数的最大值。对于一个长度为\(l(l\ge1)\)的段,它的最小操作次数为\(\l......
  • P4198 楼房重建 题解
    P4198楼房重建题解线段树二分思路考虑在线段树内维护二信息:区间斜率最大值\(mx\)区间最大斜率上升序列长度\(len\)答案即为根节点的\(len\)。考虑转移信息二:蓝色部分代表左区间的上升序列,红色是右区间的,绿色折线就是当前区间的上升序列。......
  • P2051 [AHOI2009] 中国象棋 题解
    DP。状态设计是点睛之笔。首先显然有每行或每列只能有至多\(2\)个棋子。设状态\(f_{i,j,k}\)为第\(i\)行,有\(j\)列只放了一个棋子,\(k\)列放了两个棋子。之后直接转移即可。注意边界判断。code:点击查看代码#include<bits/stdc++.h>#definelllonglong#defined......
  • Java cmd下编译乱码问题解决办法
    1、报错样式 2、解决办法1)指定字符集,如下 2)修改编码格式通过“记事本”打开—》另存为3)修改环境变量此电脑——》属性——》高级系统设置——》环境变量——》(系统环境变量)新建——》“JAVA_TOOL_OPTIONS” “-Dfile.encoding=UTF-8”如下图:——》重启cmd,再......
  • CF911F Tree Destruction
    题意:给你一棵\(n\)个结点组成的树,你需要对树进行\(n-1\)次操作,一次操作包含如下的步骤:选择两个叶子结点将这两个结点之间简单路径的长度加到答案中从树上删去两个叶子结点之一初始答案为\(0\),显然在\(n-1\)次操作之后树上只剩下一个结点。计算最大的答案,并构造一组......
  • CF1034D Intervals of Intervals 题解
    传送门CF1034DIntervalsofIntervals题目大意有\(n\)个线段,第\(i\)个是\([a_i,b_i]\)。定义区间\([l,r]\)的价值是第\(l\)个线段到第\(r\)个线段的并的长度。找出\(k\)个不同的区间,使得总价值最大。输出最大总价值。\(1\len\le3\times10^5,1\lek\le......
  • asm_second 题解(坐标转换+二维偏序)
    QuestionAsm.Def在第一象限内找到了n个可疑点。他需要为导弹规划路径。如图所示,导弹一开始在(0,0)。它只能朝着一定的方向——即严格夹在图中两条射线间的方向(白色部分)前进。注意,它不能沿着这两条射线前进,当然也不能停在原地。当导弹到达某个可疑点后,它仍然只能朝着该范围内......
  • 2023湖北CCPC省赛 蒻蒟的部分题解
    题目地址C.DarknessI题意:有一个n*n的方格,最开始全是白色,如果白色周围4格有两个黑色格子,1秒后这个白色格子会变成黑色,问如果要使全部格子都变为黑色,最开始最少需要涂黑几个格子Solution对于两个黑色格子,只有当满足\[|x_1-x_2|+|y_1-y_2|≤2\]才能涂黑以这两个格子为顶点的矩......
  • 2023 Hubei Provincial Collegiate Programming Contest题解 C F H I J K M
    补题链接:https://codeforces.com/gym/104337原文链接:https://www.eriktse.com/algorithm/1136.htmlM.DifferentBilling签到题,写几个柿子然后枚举B或C即可。#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;signedmain(){ ios::sync_with_stdio(......