Codeforces Round #777 (Div. 2)

CF1647A Madoka and Math Dad

位数越多肯定越大。但不能有两位相同,所以 1,2 交替填就好了。

CF1647B Madoka and the Elegant Gift

并查集维护联通块,记录连通块的四个方向的极值位置,然后看一下生成的这个矩形内部是否全黑。

写着很烦。

CF1647C Madoka and Childish Pranks

限制很松,可以每步使一个格子满足,发现每一排非第一列的格子都可以用 \(1 \times 2\) 的矩形解决。第一列非第一排的可以用 \(2 \times 1\) 的解决,如果第一列第一排不是白色那就必然无解。

CF1647D Madoka and the Best School in Russia

很恶心的分讨。(这场怎么那么多分讨)

先把 \(x\) 中的 \(x\) 提取出来。如果只有一个那就没救了。

如果剩下的数是个合数,那么肯定稳了。

否则如果只能提取出来两个就又没救了。

接下来只能考虑拆一个 \(d\) 出来给剩下的,因此如果 \(d\) 不是合数那就没救了。

但分了之后可能会在 \(x\) 剩下的值的影响下出现 \(d^2\) 的因子。

如果剩下的值是 \(1\) 就稳了。

否则如果 \(d\) 是剩下的数的平方,并且只能拆出三个 \(d\) 那就没救了。

否则一定有解。

int main() {
	int T;
	read(T);
	while(T -- > 0) {
		LL x , d , num = 0;
		read(x),read(d);
		while(x % d == 0) x /= d , num ++;
		if(num <= 1) puts("NO");
		else if(!pr(x)) puts("YES");
		else if(num == 2) puts("NO");
		else if(pr(d)) puts("NO");
		else if(x == 1) puts("YES");
		else {
			int t = 0;
			while(d % x == 0) t ++ , d /= x;
			if(d > 1) puts("YES");
			else if(num > 3) puts("YES");
			else if((t + 2) / 2 < t) puts("YES");
			else puts("NO");
		}
	}
	return 0;
}

CF1647E Madoka and the Sixth-graders

将基环树建出来,容易发现每轮死的数的个数一定,因此我们可以根据当前最大数确定进行轮数。

接下来发现两个个数会在中途相撞当且仅当最后终点一样。排列中每个位置的终点可以倍增计算。

然后就求出了每个位置可以被哪些初始位置到达,这些初始位置的数一定大于等于当前位置的值,因此可以贪心放置在最小位置。

然后考虑没有出现的排列中的数,可以塞进 set 里面,对从小到大每个位置找到最小的满足下界限制的值填进去就好了。

const int MAXN = 1e5 + 5;
 
int f[MAXN][31] , a[MAXN] , b[MAXN] , n , p[MAXN] , deg[MAXN];
int fina[MAXN] , Min[MAXN];
vector <int> Ori[MAXN];
 
int Calc(int x , int t) {
	for (int i = 30; i >= 0; --i) if((1 << i) <= t) {
		t -= (1 << i);
		x = f[x][i];
	}
	return x;
}
 
set <int> S;
 
int main() {
	read(n);
	int mxa = 0;
	for (int i = 1; i <= n; ++i) read(p[i]) , deg[p[i]] ++ , f[i][0] = p[i];
	for (int i = 1; i <= n; ++i) read(a[i]) , mxa = max(mxa , a[i]);
	int cnt = 0;
	for (int i = 1; i <= n; ++i) if(!deg[i]) cnt ++;
	int tn = (mxa - n) / cnt;
	for (int j = 1; j <= 30; ++j) {
		for (int i = 1; i <= n; ++i) f[i][j] = f[f[i][j - 1]][j - 1];
	}
	
	for (int i = 1; i <= n; ++i) fina[i] = Calc(i , tn) , Ori[fina[i]].push_back(i) , S.insert(i);
	
	for (int i = 1; i <= n; ++i) if(a[i] <= n) {
		S.erase(S.lower_bound(a[i]));
		sort(Ori[i].begin() , Ori[i].end());
		b[Ori[i][0]] = a[i];
		for (auto v:Ori[i]) Min[v] = a[i];
	}
	
	for (int i = 1; i <= n; ++i) {
		if(b[i]) write(b[i] , ' ');
		else {
			auto x = S.lower_bound(Min[i]);
			write(*x , ' ');
			S.erase(x);
		}
	}
	
	return 0;
}

CF1647F Madoka and Laziness

算是3000+的题目中比较容易的了。

这场怎么回事?怎么又是分讨。

注意到全局最大值一定是两个子序列其中之一的峰顶,于是答案一定是小于 \(n\) 的。

因此考虑枚举每一个值能否作为另外一个峰顶。

为了简化讨论,我们假设最大值位置为 \(x\),另一个峰顶位置为 \(y\),并且满足 \(y < x\)

至于 \(y > x\) 的部分翻转过来再跑一遍就好了。

然后考虑判断。发现 \(i > x\) 的部分我们需要将其划分为两个递减序列,于是想到使用 DilWorth 定理。

那么就是这部分的最长上升子序列长度不超过 2。经典地,我们可以推出每个数要么是前缀最小值,要么是后缀最大值。

判断右边的左边还能不能接上一段递减序列,就要看添上之后是否仍然满足上面的性质。即大于该序列的末尾的数都要是后缀最大值。

那么我们只需要预处理右侧不是后缀最大值的数当中最大的就好。

接下来考虑 \(i < y\) 的部分。发现仍然是 Dilworth 定理。

需要判断一段前缀能否在右侧接上一段上升序列的情况下能否划分为两个上升序列。同时要满足这个接上的上升序列不能接在 \(y\) 上。

那么只需要讨论插入该序列开头的情况,如果插入的序列开头比 \(y\) 小,就插在 \(y\) 的右边,然后判断是否比他大的都是前缀最大值。

否则插在左边判断。

最后就是 \(i \in (y , x)\) 的情况,我们要求出一个上升一个下降的序列,并且上升的开头尽可能大,下降的结尾要大于一个定值。

由于下降结尾大于定值,因此转换视角,从右到左看,变成了对初始的限定,上升变下降,同时要求变为下降序列的结尾尽可能大。

是个经典问题。

定义 \(f_{i,0}\) 表示 \(i\) 属于递增序列,递减序列结尾最大值。

定义 \(f_{i,1}\) 表示 \(i\) 属于递减序列,递增序列结尾最小值。

枚举 \(a_i , a_{i+1}\) 分别属于递增递减序列即可转移。

显然 \(y\) 是递增序列的结尾,然后我们就最大化了递减序列的结尾,做完了。

int solve(int x) {
	for (int i = n; i > x; --i) Max[i] = max(Max[i + 1] , a[i]);
	Min[x] = a[x];
	int mxr = 0;
	for (int i = x + 1; i <= n; ++i) {
		Min[i] = min(Min[i - 1] , a[i]);
		if(Min[i] != a[i] && Max[i] != a[i]) return 0;
		if(a[i] != Max[i]) mxr = max(mxr , a[i]);
	}
	for (int i = 1; i < x; ++i) Max[i] = max(Max[i - 1] , a[i]);
	chk[0] = 1;
	for (int i = x - 1; i >= 1; --i) Min[i] = min(Min[i + 1] , a[i]);
	for (int i = 1; i < x; ++i) {
		Mxl[i] = Mxl[i - 1];
		if(a[i] != Max[i]) Mxl[i] = max(Mxl[i] , a[i]);
	}
	for (int i = 1; i < x; ++i) chk[i] = chk[i - 1] & (a[i] > Mxl[i - 1]);
	int res = 0;
	
	f[x][0] = 0 , f[x][1] = mxr;
	// 0 a_i 属于递增序列,递减序列结尾最大值
	// 1 a_i 属于递减序列,递增序列结尾最小值 
	for (int i = x - 1; i >= 1; --i) {
		f[i][0] = 0 , f[i][1] = 1e9;
		if(a[i] > a[i + 1]) f[i][0] = max(f[i][0] , f[i + 1][0]);
		if(a[i] < a[i + 1]) f[i][1] = min(f[i][1] , f[i + 1][1]);
		if(f[i + 1][1] < a[i]) f[i][0] = max(f[i][0] , a[i + 1]);
		if(f[i + 1][0] > a[i]) f[i][1] = min(f[i][1] , a[i + 1]);
		
		if(!chk[i]) continue;
		if(f[i][0] <= Mxl[i] || (f[i][0] > a[i] && f[i][0] < Max[i])) continue; 
		res ++;
	}
	return res;
}
posted @ 2022-04-12 17:25  Reanap  阅读(32)  评论(0编辑  收藏  举报