cf1843

A. Sasha and Array Coloring

题目链接
题目大意:将数组分为若干个集合每个集合非空,求每个集合内最大和最小元素之差的和的最大值

数组排序后将最大和最小元素划为一个集合,次大次小一个集合,依次类推

// Problem: A. Sasha and Array Coloring

// Contest: Codeforces - Codeforces Round 881 (Div. 3)

// URL: https://codeforces.com/contest/1843/problem/A

// Memory Limit: 256 MB

// Time Limit: 1000 ms

//

// Powered by CP Editor (https://cpeditor.org)

  

#include <iostream>

#include <cstring>

#include <queue>

#include <algorithm>

#include <cmath>

#include <stack>

#include <vector>

#include <map>

#include <set>

using namespace std;

#define x first

#define y second

typedef pair<int, int> PII;

typedef long long LL;

int T;

void output(vector<int> q)

{

for(int i=0;i<q.size();i++){

cout<<q[i]<<" ";

}

cout<<endl;

}

int main()

{

cin>>T;

while(T--)

{

int n;

cin>>n;

vector<int> q;

for(int i=0;i<n;i++)

{

int x;

cin>>x;

q.push_back(x);

}

sort(q.begin(),q.end());

// output(q);

int sum=0;

int r=q.size()-1;

int l=0;

for(l=0;l<=r;l++,r--)

{

sum+=q[r]-q[l];

}

cout<<sum<<endl;

}

  

return 0;

}

B. Long Long

题目链接
题目大意:给定一个整数序列,每次可以将一个连续的区间符号取反,求最少需要几步可以将所有负数变为正数。因为要求在有限步内最大的和,所以转化为将所有的负数转化为正数。

首先考虑0换号无所谓,所以先把0筛掉,接着求连续负数的字段有多少个即可。

// Problem: B. Long Long

// Contest: Codeforces - Codeforces Round 881 (Div. 3)

// URL: https://codeforces.com/contest/1843/problem/B

// Memory Limit: 256 MB

// Time Limit: 2000 ms

//

// Powered by CP Editor (https://cpeditor.org)

  

#include <iostream>

#include <cstring>

#include <queue>

#include <algorithm>

#include <cmath>

#include <stack>

#include <vector>

#include <map>

#include <set>

using namespace std;

#define x first

#define y second

typedef pair<int, int> PII;

typedef long long LL;

const int N=2e5+10;

int T;

int a[N];

int main()

{

cin>>T;

while(T--)

{

int n;

int cnt=0;

LL sum=0,res=0;

cin>>n;

for(int i=0;i<n;i++)

{

int x;

cin>>x;

if(x)

{

a[cnt++]=x;

sum+=abs(x);

}

}

vector<int> q;

if(cnt!=0)

{

q.push_back(a[0]<0?1:0);//将所有的连续负数子段用1表示,正数字段用0表示,连续负数字段的个数即为vector中的前缀和

for(int i=1;i<cnt;i++)

{

if((a[i]<0&&q[q.size()-1])||(a[i]>0&&q[q.size()-1]==0)) continue;

else

{

if(a[i]<0&&q[q.size()-1]==0) q.push_back(1);

else if(a[i]>0&&q[q.size()-1]==1) q.push_back(0);

}

}

}

for(int i=0,len=q.size();i<len;i++){

res+=q[i];

}

cout<<sum<<" "<<res<<endl;

}

  

return 0;

}

C. Sum in Binary Tree

题目链接
大意:求二叉树中到指定节点的路径的所有结点的序号之和

二叉树一个节点的父节点就是当前节点除2,左右孩子均是如此。那么不断除2直至为1所得到的结果序列就是二叉树从根节点到指定节点的路径。

// Problem: C. Sum in Binary Tree

// Contest: Codeforces - Codeforces Round 881 (Div. 3)

// URL: https://codeforces.com/contest/1843/problem/C

// Memory Limit: 256 MB

// Time Limit: 1000 ms

//

// Powered by CP Editor (https://cpeditor.org)

  

#include <iostream>

#include <cstring>

#include <queue>

#include <algorithm>

#include <cmath>

#include <stack>

#include <vector>

#include <map>

#include <set>

using namespace std;

#define x first

#define y second

typedef pair<int, int> PII;

typedef long long LL;

int T;

int main()

{

cin>>T;

while(T--)

{

LL t;

cin>>t;

LL res=0;

while(t)

{

res+=t;

t/=2;

}

cout<<res<<endl;

}

return 0;

}

D. Apple Tree

题目链接

大意:一个苹果树只有两个叶子节点,可以通过摇晃苹果树来得到苹果
每次摇晃有以下两种情况:

  1. 若其为叶子节点则直接掉落
  2. 否则有一个孩子就落到孩子节点上。(多个孩子则随机到一个孩子中)

思路:由于是一棵树且只有两个叶子节点,则只有一个点有分叉的结点其余均只有一个孩子,接下来只需递归的确定以每个结点为根的子树有多少个叶子节点即可。答案只可能有1,2,4三种情况。

// Problem: D. Apple Tree

// Contest: Codeforces - Codeforces Round 881 (Div. 3)

// URL: https://codeforces.com/contest/1843/problem/D

// Memory Limit: 512 MB

// Time Limit: 4000 ms

//

// Powered by CP Editor (https://cpeditor.org)

  

#include <iostream>

#include <cstring>

#include <queue>

#include <algorithm>

#include <cmath>

#include <stack>

#include <vector>

#include <map>

#include <set>

using namespace std;

#define x first

#define y second

typedef pair<int, int> PII;

typedef long long LL;

const int N=2e5+10;

vector<int> g[2<<17];

int ch[2<<17];

int dfs(int u,int p)

{

bool fla=false;//判断是否为非叶子结点

for(int v:g[u])

{

if(v!=p)

{

fla=true;

ch[u]+=dfs(v,u);

}

}

if(!fla) ch[u]=1;//只有叶子结点时fla才会保持false的状态

return ch[u];

}

int main()

{

int T; cin>>T;

while(T--)

{

int n;

cin>>n;

for(int i=0;i<n;i++) g[i].clear();

memset(ch,0,sizeof(ch));

for(int i=1;i<n;i++)

{

int u,v; cin>>u>>v;

u--,v--;

g[u].push_back(v);

g[v].push_back(u);

}

dfs(0,-1);

int m; cin>>m;

for(int i=0;i<m;i++)

{

int a,b; cin>>a>>b;

cout<<(LL)ch[--a]*ch[--b]<<endl;

}

}

  

return 0;

}

E. Tracking Segments

题目链接
大意:给定一个长度为n初始值全为0的序列a和m个区间,再给定q个步骤xi(1<=i<=q) 依次将a[xi] 变为1,问最小要到第几步在m个区间内会有一个区间出现1的个数大于0的个数的情况。

二分答案

// Problem: E. Tracking Segments

// Contest: Codeforces - Codeforces Round 881 (Div. 3)

// URL: https://codeforces.com/contest/1843/problem/E

// Memory Limit: 256 MB

// Time Limit: 2000 ms

//

// Powered by CP Editor (https://cpeditor.org)

  

#include <iostream>

#include <cstring>

#include <queue>

#include <algorithm>

#include <cmath>

#include <stack>

#include <vector>

#include <map>

#include <set>

using namespace std;

#define x first

#define y second

typedef pair<int, int> PII;

typedef long long LL;

const int N=1e5+10;

int T;

int n,m,q;

int sl[N],sr[N];

int a[N],x[N];

int s[N];

bool check(int mid)

{

for(int i=0;i<mid;i++)

{

a[x[i]-1]=1;

}

for(int i=0;i<n;i++)

{

s[i+1]=s[i]+a[i];

}

for(int i=0;i<m;i++)

{

int cnt=s[sr[i]]-s[sl[i]-1];

if(cnt>(sr[i]-sl[i]+1)-cnt)

{

return true;

}

}

return false;

}

int main()

{

ios::sync_with_stdio(false);

cin.tie(0);

cin>>T;

while(T--)

{

cin>>n>>m;

for(int i=0;i<m;i++)

{

cin>>sl[i]>>sr[i];

}

cin>>q;

for(int i=0;i<q;i++)

{

cin>>x[i];

}

int l=0,r=q+1;

while(l<r)

{

for(int i=0;i<n;i++) a[i]=0;

int mid=(l+r)>>1;

if(check(mid)) r=mid;

else l=mid+1;

}

if(r==q+1) r=-1;

cout<<r<<endl;

}

  

return 0;

}

F1. Omsk Metro (simple version)

题目链接
大意:给定一棵树初始时只有一个根节点权值为1,每一次会发生两种操作中的一种:

  1. 结点vi后按序号加一个权重为x的结点
  2. 询问从根节点到指定结点是否存在一子段使得这个子段的节点的权重和为指定的k

分析:

  1. 权值x{1,1} 可以得出结论是路径上最大字段和和最小字段和之间任意整数均可取到,因为可以取空,所以0也可以取到。
  2. 由于起点固定,所以最大最小子段只需要和0比较而不需要和之前子段的最大最小子段和比较
// Problem: F1. Omsk Metro (simple version)

// Contest: Codeforces - Codeforces Round 881 (Div. 3)

// URL: https://codeforces.com/contest/1843/problem/F1

// Memory Limit: 512 MB

// Time Limit: 2000 ms

//

// Powered by CP Editor (https://cpeditor.org)

  

#include <iostream>

#include <cstring>

#include <queue>

#include <algorithm>

#include <cmath>

#include <stack>

#include <vector>

#include <map>

#include <set>

using namespace std;

#define x first

#define y second

typedef pair<int, int> PII;

typedef long long LL;

const int N=2e5+10;

int q,n;

vector<int> g[N];

int x[N],u[N],v[N],k[N];

int MX[N],MN[N];

void dfs(int u,int p,int mx,int mn)

{

mx=max(mx+x[u],0);

mn=min(mn+x[u],0);

MX[u]=mx,MN[u]=mn;

if(p!=-1)

{

MX[u]=max(MX[u],MX[p]);

MN[u]=min(MN[u],MN[p]);

}

for(int V:g[u]) dfs(V,u,mx,mn);

}

int main()

{

ios::sync_with_stdio(false);

cin.tie(0);

int T; cin>>T;

while(T--)

{

cin>>q;

n=0;

x[0]=1;

g[0].clear();

int sz=0;

for(int i=0;i<q;i++)

{

char op[2];

cin>>op;

if(op[0]=='+')

{

int v,val;

cin>>v>>val;

g[--v].push_back(++n);

g[n].clear();

x[n]=val;

}

else

{

cin>>u[sz]>>v[sz]>>k[sz];

u[sz]--,v[sz]--;

sz++;

}

}

dfs(0,-1,0,0);

for(int i=0;i<sz;i++)

{

if(MN[v[i]]<=k[i]&&k[i]<=MX[v[i]]) puts("yes");

else puts("no");

}

}

  

return 0;

}

F2. Omsk Metro (hard version)

题目链接
大意:是上面的hard版,唯一的不同是起始点可变不再为第一个顶点
思路:按照灵神的讲解先把树上问题转化为数组问题

按照数组来做这个问题时,由F1结论就是找在给定区间[u,v]内的最大和最小子段和是否涵盖住了价值k,可以用线段树求解区间最大最小字段和

接下来就是将将其移到树上,可以使用树上倍增的思想来做这道题]
F1hard
树上倍增lca在增加结点时可以顺带维护从本节点往上走2i步的段的最大最小子段和,最大最小子段和的求法和数组上的类似采用分治的思想

最大最小子段和由三种情况
1. 来自最左端
2. 来自最右端
3. 两者合并后中间
对于第三种情况要维护最大和最小前后缀,以便合并后使用

在最后合并时记得将u反向。

// Problem: F2. Omsk Metro (hard version)
// Contest: Codeforces - Codeforces Round 881 (Div. 3)
// URL: https://codeforces.com/contest/1843/problem/F2
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int N=2e5+10,M=19;
int dep[N],fa[N][M],a[N];
struct info
{
	int sum,maxpref,maxsuff,maxsegsum,minpref,minsuff,minsegsum;
}lifts[N][M];
info make(int x)
{
	int a=max(x,0);
	int b=min(x,0);
	return info{x,a,a,a,b,b,b};
}
info rev(const info& a)
{
	return info{a.sum, a.maxsuff, a.maxpref, a.maxsegsum, a.minsuff, a.minpref, a.minsegsum};
}
info operator + (const info& a, const info& b)//重载加法运算符
{
	return info{
		a.sum+b.sum,
		max(a.maxpref,a.sum+b.maxpref),
		max(b.maxsuff,a.maxsuff+b.sum),
		max({a.maxsegsum,b.maxsegsum,a.maxsuff+b.maxpref}),
		min(a.minpref,a.sum+b.minpref),
		min(b.minsuff,a.minsuff+b.sum),
		min({a.minsegsum,b.minsegsum,a.minsuff+b.minpref}),
	};
}
void output(int cnt)
{
	for(int i=0;i<cnt;i++)
	{
		for(int j=0;j<=2;j++)
		{
			cout<<fa[i][j];
		}
		cout<<endl;
	}
	// for(int i=0;i<cnt;i++)
	// {
		// cout<<a[i]<<" ";
	// }
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;cin>>T;
	while(T--)
	{
		int q; cin>>q;
		for(int i=0;i<M;i++) lifts[0][i]=make(0);
		a[0]=1;
		dep[0]=0;
		int cnt=1;
		while(q--)
		{
			char op[2]; cin>>op;
			if(op[0]=='+')
			{
				int v,x; cin>>v>>x;
				v--;
				a[cnt]=x;
				fa[cnt][0]=v;
				dep[cnt]=dep[v]+1;
				lifts[cnt][0]=make(x);
				for(int i=1;i<M;i++)//这里倍增时顺带维护从该节点出发往上2^i步的结点的最大最小子段
				{
					fa[cnt][i]=fa[fa[cnt][i-1]][i-1];
					lifts[cnt][i]=lifts[fa[cnt][i-1]][i-1]+lifts[cnt][i-1];
				}
				cnt++;
			}
			else
			{
				int u,v,k; cin>>u>>v>>k;
				u--,v--;
				info L=make(0);
				info R=make(0);
				if(dep[u]>dep[v]) swap(u,v);//寻找LCA过程
				for(int i=M-1;i>=0;i--)
				{
					if(dep[v]-dep[u]>=(1<<i))
					{
						R=lifts[v][i]+R;
						v=fa[v][i];
					}
				}
				for(int i=M-1;i>=0;i--)
				{
					if(fa[u][i]!=fa[v][i])
					{
						L=lifts[u][i]+L;
						R=lifts[v][i]+R;
						u=fa[u][i];
						v=fa[v][i];
					}
				}
				if(u!=v)
				{
					L=lifts[u][0]+L;
					R=lifts[v][0]+R;
					u=fa[u][0];
					v=fa[v][0];
				}
				info tot=rev(L)+make(a[u])+R;//这里将左半侧的路径方向反转,加上祖先的权值再加上右半侧即为答案
				cout<<(k>=tot.minsegsum&&k<=tot.maxsegsum ? "YES":"NO")<<endl;
			}
		}
		// cout<<cnt<<endl;
		// output(cnt);
	}

	return 0;
}


posted @   viewoverlook  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
欢迎阅读『cf1843』
点击右上角即可分享
微信分享提示