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
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].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
#include
#include
#include
#include
using namespace std;
const int N = 100;
vector e[N], ans;//e用来存边
int ind[N], fg[N][N] {{0}}; //ind是存每个点的入度,fg[i][j]用来标记i是否指向过j,防止重复
queue 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> 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; i0) ret = 1; //如果存在标记为1,表示没有答案
if(ret) cout << "No Answer\n";
else for(auto &i:ans) cout << KeyTStr(i);
cout << endl;
return 0;
}