码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces Round 701 (Div2) 题解

时间:2021-02-16 12:45:29      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:The   观察   快速   增加   class   通过   正整数   多少   最优   

Codeforces Round 701 (Div2) 题解

\(zhanglichen\ 2021.2.13\)

\(A.Add\ and\ Divider\)

题意:

您有两个正整数\(a\)\(b\)

您可以执行两种操作:

\(a =?a/b?\)(用a和b之间的除法的整数部分替换a)
\(b = b + 1\)(将b增加1)
找到使\(a = 0\)所需的最小操作数。

题解:

我的做法是枚举\(b\)的增加次数。这个做法不是很优,但是比较无脑。

\(B.Replace\ and\ Keep\ Sorted\)

题意:

给出正整数\(k\),如果满足以下条件,则两个数组称为\(k\)-相似:

(1)都严格递增。

(2)长度相同。

(3)所有元素都是\(1\)\(k\)之间的正整数。

(4)它们在一个位置上完全不同。

给出一个严格递增的数组和\(q\)次查询,每次查询一段区间\(l\)\(r\),询问存在多少个数组\(b\),使得\(b\)\(a[l..r]\)相似。

题解:

依次枚举区间的每个位置,计算当这个位置不同时有多少种\(b\)

为了保证数组的单调性,每个位置的取值是有一个上下区间的。对所有区间求和即可。

注意到除了首尾,每个位置的区间是固定的,所以可以用前缀和预处理。

时间复杂度\(O(n+q)\)

\(C.Floor\ and\ Mod\)

题意:

如果\(?a/b?= a%b\),则一对正整数(\(a\)\(b\))被称为特殊。

给出两个整数x和y。 求出特殊对(a,b)的数量,使得\(1≤a≤x\)\(1≤b≤y\)

题解:

可以转化为\(a=k(b+1)\),其中\(k\)小于\(b\)

枚举\(a\)\(b\)的时间复杂度是无法接受的,一种巧妙的方法是枚举\(k\)

时间复杂度\(O(\sqrt x)\)

\(D.Mutliples\ and\ Power\ Differences\)

题意:

给出一个矩阵\(a\),每个元素小于等于\(16\)

请构造一个矩阵\(b\),使得\(b_{i,j}\)\(a_{i,j}\)的倍数,同时相邻的\(b_{i,j}\)之间的差要是某个正数的4次方。

题解:

计算出1到16的公倍数\(x\),对每个\((i+j)\%2=0\)的位置赋值\(x-a_{i,j}^4\),对剩余位置赋值\(x\)即可。

之前碰到过一道一模一样的,奈何比赛的时候被\(C\)暗算了,没开\(D\),草。

\(E.Move\ and\ Swap\)

题意:

给出一棵树,一开始R和B都在树根1号点。

每次操作分为以下三步:

(1)将R移到R所在节点的任意孩子

(2)将B移到下一层的任意节点(即到1号点的距离比当前位置多1)

(3)可以交换R和B的位置

每次移动后,获得\(|a_R-a_B|\)的收益。

询问怎么移动可以获得最大收益。

题解:

先把节点按照所在层数分组。

定义\(f(i)\)为当做完上一轮操作后,R在第\(i\)个点时的最大收益。

如果知道上一层节点中的每个\(f(j)\),可以通过\(f(j)\)来计算\(f(i)\)

可以分为以下两种情况:

(1)如果在第2步以后,\(R\)在节点\(i\),说明\(R\)的位置没有变,B应当在同一组节点中的最小点或最大点。

(2)如果在第2步以后,\(B\)在节点\(i\)\(R\)在节点\(j(d(1,j)=d(1,i))\),那么你必须最大化\(d_{father(j)}+|a_j-a_i|\)

这可以对当前层的\(a_i\)排序,分别计算\(a_j \leq a_i\)\(a_i \leq a_j\)两种情况。

对当前层的每个\(i\),最优的\(j\)不会改变。

以上是官方题解内容,这题还没理解...

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
vector<int> g[maxn],d[maxn];
int dep[maxn],m,n;
long long f[maxn],a[maxn];
void dfs (int x,int f) {
	d[dep[x]].push_back(x);
	m=max(m,dep[x]);
	for (int y:g[x]) {
		if (y==f) continue;
		dep[y]=dep[x]+1;
		dfs(y,x);
	}
}
int cmp (int x,int y) {
	return a[x]<a[y];
}
int main () {
	int _;
	scanf("%d",&_);
	while (_--) {
		scanf("%d",&n);
		m=0;
		for (int i=1;i<=n;i++) g[i].clear(),d[i].clear(),f[i]=0,dep[i]=0;
		for (int i=2;i<=n;i++) {
			int x;
			scanf("%d",&x);
			g[i].push_back(x);
			g[x].push_back(i);
		}
		for (int i=2;i<=n;i++) scanf("%d",a+i);
		dep[1]=1;
		dfs(1,0);
		for (int i=m;i>1;i--) {
			long long A=-1e18,B=-1e18;
			sort(d[i].begin(),d[i].end(),cmp);
			for (int x:d[i]) {
				A=max(A,f[x]+a[x]);
				B=max(B,f[x]-a[x]);
			}
			for (int x:d[i-1]) {
				for (int y:g[x]) {
					if (dep[y]==dep[x]+1) {
						f[x]=max(f[x],f[y]+abs(a[d[i][0]]-a[y]));
						f[x]=max(f[x],f[y]+abs(a[d[i].back()]-a[y]));
						f[x]=max(f[x],A-a[y]);
						f[x]=max(f[x],B+a[y]);
					}
				}
 			}
		}
		printf("%lld\n",f[1]);
	}
}

\(F.Copy\ or\ Prefix\ Sum\)

题意:

给出一个数组\(b\)

\(b_i\)可以等于\(a_i\),也可以等于\(\sum_{j=1}^ia_j\)

询问有多少个数组\(a\)

题解:

对每个位置\(i\),你可以选择\(a_i=b_i\)\(a_i=b_i-\sum_{k=1}^{i-1}a_k\)

如果\(\sum_{k=1}^{i-1}a_k=0\),那么这两种选择构成的数组是一样的,对答案的贡献是1。

一种\(O(n^2logn)\)的做法:

定义\(f(i,j)\)是当前在第\(i\)位,前缀和是\(j\)的情况有多少种。

状态转移方程:

如果你选择\(b_i=a_i\),同时\(j \neq 0\)

\(f(i+1,j+b_i)=f(i,j)\)

如果你选择\(b_i=\sum_{k=1}^ia_k\)

\(f(i+1,b_i)=f(i,j)\)

\(Map\)实现状态转移方程可以把时间复杂度控制在\(O(n^2logn)\)

观察状态转移方程,就是把当前位置的所有\(j \neq 0\)\(f(i,j)\)变成\(f(i+1,j+b_i)\),同时把所有\(f(i,j)\)加给\(f(i+1,b_i)\)

第二步就是当前的情况数,第一步就是当前的情况数减去\(j=0\)的情况数。

合并就是:下一步的情况数=当前的情况数*2-\(j=0\)的情况数。

怎么快速计算\(j=0\)的情况数:

考虑到每一步,所有状态统一加\(b_i\),那么第二步可以转化为把所有的\(f(i,j)\)加给\(f(i+1,0)\),然后把两步的所有情况都变成\(f(i+1,j+b_i)\)

维护一个懒惰标记,标记当前一共加了多少(即\(b\)的前缀和)。可以直接用\(lazy\)表示。

然后转移的时候,\(f(lazy)\)就表示当前\(j=0\)的情况,每次转移把\(lazy\)\(b_i\)即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
const int mod=1e9+7;
int b[maxn],n,t;
map<long long,long long> f;
int main () {
	scanf("%d",&t);
	while (t--) {
		scanf("%d",&n);
		for (int i=1;i<=n;i++) scanf("%d",b+i);
		f.clear();
		long long lazy=0;
		long long ans=1;
		f[0]=1; 
		for (int i=1;i<=n;i++) {
			long long tt=ans;
			ans=(ans*2-f[lazy]+mod)%mod;
			f[lazy]=tt%mod;
			lazy-=b[i];
		}
		ans%=mod;
		printf("%lld\n",ans);
	}
}

Codeforces Round 701 (Div2) 题解

标签:The   观察   快速   增加   class   通过   正整数   多少   最优   

原文地址:https://www.cnblogs.com/zhanglichen/p/14400270.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!