2021辽宁省赛-I.完美主义-M.比赛

I.完美主义

链接:完美主义 (nowcoder.com)
来源:牛客网

题目描述 
阿强采摘了一些苹果,并把他们分堆排成了一行,从左往右编号为第 1 … 𝑛 堆,其中第𝑖堆苹果有ai个。
完美主义者阿珍看到这些苹果,觉得他们摆放的非常杂乱。她要求阿强进行如下的操作。
对某堆苹果进行调整:阿强将会将第𝑖堆苹果调整成bi个;
对阿珍询问做出答复:其中每次询问表示为[𝑙, 𝑟],表示询问第𝑙堆到第𝑟堆之间的苹果数量是否满足al≤al+1≤⋯≤ar−1≤ar,如果满足则称为完美。
 

输入描述:
第一行两个整数n, q(1≤n,q≤3∗10e5 ),表示苹果的堆数和操作的个数;
第二行n个整数表示ai 。
以下𝑞行,每行3个整数,第一个整数为opt;
若opt = 1,之后两个整数i, bi ,表示将第𝑖堆苹果调整为bi个;
若opt = 2,之后两个整数𝑙, 𝑟,表示对[𝑙, 𝑟]之间的苹果堆进行询问。(1≤ai,bi≤10e9)
 

输出描述:
输出一共𝑞行,每行一个 Yes 或者 No,表示每个询问对应区间是否完美。

示例1
输入
7 4
1 2 2 4 3 4 5
1 1 4
2 1 7
2 6 7
2 4 7
输出
No
Yes
No

完美的条件是al<=al+1<=...<=ar-1<=ar,看到这种条件第一反应就是差分,如果原数组i位置上的数ai大于等于前面的数,则差分数组中bi>=0, 反之bi<0。但题目要求判断的不是某两个连续点的单调性,而是一长段区间,所以要只靠差分数组判断的话,需要遍历区间[l, r],时间复杂度过大。

因此需要一种方法,可以快速的求得区间[l, r]的单调性,而线段树就可以满足这种需求,因此只要用线段树维护差分数组就行了。

我们可以在线段树节点的结构体中定义一变量fg用来记录当前区间是否有数小于0(因为是用线段树维护的差分数组,小于0时代表不递增),小于0就标记为1,反之为0。 每次回溯更新时 fg = 左儿子的fg | 右儿子的fg 即可。(如果左儿子或右儿子有一个fg为1,则代表它俩的父节点fg也为1,所以或运算). 

每次执行操作1时,是改变原来数组中ai的值为k, 而差分数组bi = ai - ai-1, bi+1 = ai+1 - ai, 所以每次操作差分数组更新为bi = bi - ai + k,bi+1 =  bi+1 + ai - k, 并且对线段树相应位置i和i+1进行单点修改.进行操作2时,则和一般线段树区间求和操作一样,不过是返回的是fg。  

代码如下:

#include<iostream>
using namespace std;
const int N = 3e5+5;
struct{
	int l, r;
	int fg; //用来标记区间内是否存在负数 
} lt[4*N];
int a[N], b[N];
void Build(int l, int r, int p)
{
	lt[p].l = l, lt[p].r = r;
	if(l==r)
	{
		if(b[l]<0) lt[p].fg = 1; //存在负数等于1 
		else lt[p].fg = 0; //反之为0 
		return ;
	}
	int mid = l + r >> 1;
	Build(l, mid, p<<1);
	Build(mid+1, r, p<<1|1);
	lt[p].fg = lt[p<<1].fg | lt[p<<1|1].fg;
}
void Change(int p, int x, int val)
{
	if(lt[p].l==lt[p].r)
	{
		if(val<0) lt[p].fg = 1;
		else lt[p].fg = 0;
		return ;
	}
	if(x<=lt[p<<1].r) Change(p<<1, x, val);
	else Change(p<<1|1, x, val);
	lt[p].fg = lt[p<<1].fg | lt[p<<1|1].fg; 
}
int Get(int p, int l, int r)
{
	if(l>lt[p].r || r<lt[p].l) return 0;
	if(l<=lt[p].l && r>=lt[p].r) return lt[p].fg;
	
	int Lfg = 0, Rfg = 0;
	if(l<=lt[p<<1].r) Lfg = Get(p<<1, l, r);
	if(r>=lt[p<<1|1].l) Rfg = Get(p<<1|1, l, r);
	return Lfg | Rfg;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int n, q;
	cin >> n >> q;
	for(int i=1; i<=n; i++) cin >> a[i], b[i] = a[i] - a[i-1]; //求差分数组 
	Build(1, n, 1); //用差分数组构建线段树 
	
	while(q --)
	{
		int op, x, y;
		cin >> op >> x >> y;
		if(op==1)
		{
			b[x+1] = b[x+1] + a[x] - y; //更新差分数组 
			b[x] = b[x] - a[x] + y;
			a[x] = y; //更新原数组 
			Change(1, x+1, b[x+1]); //修改线段树中对应的点 
			Change(1, x, b[x]);
		}
		else
		{
			if(Get(1, x+1, y) && x!=y) cout << "No"; //如果返回的是1,代表差分数组中存在负数,也就是不完美的 
			else cout << "Yes";
			cout << endl;
		}
	}
	return 0;
}

M.比赛!

链接:比赛! (nowcoder.com)
来源:牛客网

题目描述

一场比赛刚刚结束。一些参赛者参加了比赛。比赛没有平局,每个参赛者在不同的时间内完成比赛。
每个参赛者被分配一个不同的字符表示他的身份(大小写字母或数字)。因此,参赛者完成比赛的顺序可以用一个字符串表示,其中每个字符只包含一次。
(字符串的第一个字符是比赛的获胜者,依此类推。例如,如果"x7X"是一场比赛的结果,则参赛者"x"获胜,参赛者"7"排名第二,参赛者"X"排名第三。)

阿强也是参赛者,想知道比赛结果。他决定采访一些参赛者,并根据他们能告诉你的信息推断出结果。

他碰巧采访的每个参赛者只记得另外两个参赛者:一个在他们之前完成,另一个在他们之后完成。例如,参赛者X可以给他信息"我在参赛者Y和Z之间的某个地方完成了比赛"。我们将此信息简称为"X:YZ"。请注意,顺序很重要:"X:ZY"与"X:YZ"的信息不同。
每次这样的采访都会告诉我们一些关于比赛结果的信息。例如,如果我们有参赛者a、b、c、d、X、Y、Z和信息"X:YZ",比赛的一些可能结果是"YaXbcZd"、"abcdYXZ"和"YdaXbZc"。
另一方面,以下结果不再可能:"XYZabcd"、"ZaXbcYd"和"YaZbXdc"。
他将获得采访者的记忆。记忆的每个元素都是一次访谈的结果,形式为"X:YZ"。请注意,同一名参赛者可能接受过多次采访,并且他们在每次采访时可能提供了不同的信息。

假设所有参赛者的集合就是那些出现在记忆中的参赛者的集合。(所有人,不仅仅是那些接受采访的人。) 请输出一种可能的结果使得满足所有采访,如果有多种输出任意一种。如果没有输出"No Answer"

输入描述:

 
 

第一行一个整数n (1≤n≤500)。表示比赛结果的信息数量。

接下来n行每行一个形如X:YZ字符串,表示一个信息。

输出描述:

输出一个字符串表示答案。如果没有正确的比赛结果输出“No Answer”

示例1

输入

2
B:AD
7:BD

输出

AB7D

该题是拓扑排序模板题,让排前面的点指向排后面的点,然后逐一输出入度为0的点,如果所有的点入度都不为0,则意味着形成了环,没有答案。 

代码如下:

#include<iostream>
#include<cstring>
#include<string> 
#include<queue>
#include<vector>
using namespace std;
const int N = 100;
vector<int> e[N], ans;//e用来存边
int ind[N], fg[N][N] {{0}}; //ind是存每个点的入度,fg[i][j]用来标记i是否指向过j,防止重复
queue<int> q; //用来存入度为0的点 
string s;
int GetKey(char c) //讲字符转换成相应的数字 
{
	if(c<='Z' && c>='A') return c-'A';
	if(c<='z' && c>='a') return c-'a'+26;
	if(c<='9' && c>='0') return c-'0'+52;
}
char KeyTStr(int x) //把数组重新转换为字符 
{
	if(x<=25) return x+'A';
	if(x>25 && x<=51) return x-26+'a';
	if(x>51) return x-52+'0';
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int n;
	cin >> n;
	memset(ind, -1, sizeof(ind)); //初始化为-1,这里主要是为了记录题目没给出的编号。没给出的编号为-1 
	for(int i=0; i<n; i++)
	{
		cin >> s;
		int a = GetKey(s[0]), b = GetKey(s[2]), c = GetKey(s[3]); //求三个编号的数字 
		if(ind[a]<0) ind[a] = 0; //如果没有别标记过则初始话为0 
		if(ind[b]<0) ind[b] = 0;
		if(ind[c]<0) ind[c] = 0;
		if(!fg[a][c]) e[a].push_back(c), fg[a][c] = 1, ind[c] ++; //fg标记,并且被指向的点入度加一 
		if(!fg[b][c]) e[b].push_back(c), fg[b][c] = 1, ind[c] ++;
		if(!fg[b][a]) e[b].push_back(a), fg[b][a] = 1, ind[a] ++;
	}
	for(int i=0; i<=62; i++)
		if(!ind[i]) q.push(i); //存入度为0的点 
	while(!q.empty())
	{
		int k = q.front();
		ans.push_back(k); //存答案 
		q.pop();
		for(int i=0; i<e[k].size(); i++) //遍历该点所指向的所有的点,并且被指向的入度减一
		{
			int v = e[k][i];
			ind[v] --;
			if(!ind[v]) q.push(v);
		}
	}
	int ret = 0; //用来标记是否存在还有点入度大于0 
	for(int i=0; i<=62; i++)
		if(ind[i]>0) ret = 1; //如果存在标记为1,表示没有答案 
	if(ret) cout << "No Answer\n";
	else for(auto &i:ans) cout << KeyTStr(i);
	cout << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值