Codeforces Round #701 (Div. 2) A~E 题解

A. Add and Divide

思路

对于\(b=1\)的情况想都不用想肯定是要多给他加一下的,那么在最小的时候也就是\(b=2\)时最多也就除个30来次,所以\(b\)增加的操作次数一定不会特别多,直接枚举就可以了.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

ll solve(ll a,ll b)
{
	int res = 0;
	while(a)
	{
		a /= b;
		++res;
	}
	return res;
}

int main() 
{
	int T;scanf("%d",&T);
	while(T--)
	{
		ll a,b;scanf("%lld%lld",&a,&b);
		ll res = 1e9;
		forn(d,b == 1,100)	res = min(res,solve(a,b + d) + d);
		printf("%lld\n",res);
	}
	return 0;
}

B. Replace and Keep Sorted

给定一个\(k\),定义两个数组\(k\)相似,当且仅当

  • 两者都是严格递增的
  • 两者长度相同
  • 所有元素在\(1\)\(k\)之间(闭区间)
  • 两个数组只恰好在一个位置不同.

给定\(q\)个询问,每个询问给出\([l,r]\),求对于子数组\([l,r]\)部分有多少个数组\(b\)与之是\(k\)相似的.

思路

对每个元素求出他可以上升和下降的个数,由于恰好只有一个位置这个条件非常强所以统计起来也比较简单.对于夹在中间的元素来说:上升时不能比下一个更大,下降时不能比前一个最小.特别的,对于每个询问处于端点的两个元素不需要考虑左端点下降和右端点上升.中间的直接预处理统计即可.

这个做法写出来还可以很容易注意到一个缺陷:\(l\)\(r\)的取值很近的时候可能会出问题,写一个样例就很容易找出来了.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
#define forr(i,x,n)	for(int i = n;i >= x;--i)

const int N = 1e5+7;
ll up[N],dw[N],a[N];

int main() 
{
	int n,q,k;scanf("%d%d%d",&n,&q,&k);
	forn(i,1,n)	scanf("%lld",&a[i]);a[n + 1] = k;
	
	forn(i,2,n)	up[i] = max(a[i] - a[i - 1] - 1,0ll);
	forr(i,1,n - 1)	dw[i] = max(a[i + 1] - a[i] - 1,0ll);
	
	forn(i,1,n)	up[i] += up[i - 1],dw[i] += dw[i - 1];
	
	forn(i,1,q)
	{
		int l,r;scanf("%d%d",&l,&r);
		ll res = a[l] - 1 + max(0ll,a[min(l + 1,r)] - a[l] - 1);
		res += max(0ll,a[r] - a[max(r - 1,l)] - 1) + k - a[r];
		if(r - 1 >= l + 1)	res += up[r - 1] - up[l] + dw[r - 1] - dw[l];
		printf("%lld\n",res);
	}
	return 0;
}

C. Floor and Mod

\((a,b)\)满足\(a/b = a \% b\),则称为牛逼数对,求\(1 \leq a \leq x\)\(1 \leq b \leq y\)的牛逼数对的个数.

式中为下取整.

思路

\(a \% b = a - (a / b) * b\)(式中为下取整).调整一下可以拿到\(a / b(↓) = a / (b + 1)\).枚举\(k=a / b(↓) = a / (b + 1)\),那么有\(a = k*b+k\).根据\(k=a\%b\)还有\(k < b\),根据不等式放缩可以拿到\(k^2 < a \leq x\),那么可以推出\(k\)有一个上界\(\sqrt x\).由于\(k\)只有根号种取值,所以做法显然是枚举其所有取值再看合法数对的个数:可以通过不等式推出\(b\in (k,min(y,x / k - 1)]\).合法数对的个数等于\(b\)的合法取值的个数,统计答案即可.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	


int main() 
{
	int T;scanf("%d",&T);
	while(T--)
	{
		ll x,y;scanf("%lld%lld",&x,&y);
		ll res = 0;
		for(ll k = 1;k <= x / k;++k)	res += max(0ll,min(x / k - 1,y) - k);
		printf("%lld\n",res);
	}
	return 0;
}

D. Multiples and Power Differences

思路

为了满足所有元素都是倍数的条件以及取值小的异常,不难想到使用\(lcm\)构造.剩下的只有如何满足差值都是某个数的四次方了:我们只需要对整个矩阵看作是二染色,某些点放上\(lcm+a[i][j]^4\)就可以了.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
#define forr(i,x,n)	for(int i = n;i >= x;--i)

const int N = 505;
int a[N][N];

int gcd(int x,int y)
{
	if(y == 0)	return x;
	return gcd(y,x % y);
}

int lcm(int x,int y)
{
	return x * y / gcd(x,y);
}

int main() 
{
	int r = 1;
	forn(i,1,16)	r = lcm(r,i);
	int n,m;scanf("%d%d",&n,&m);
	forn(i,1,n)	forn(j,1,m)	scanf("%d",&a[i][j]);
	forn(i,1,n)
	{
		forn(j,1,m)	printf("%d ",(i + j) % 2 == 0 ? r : r + a[i][j] * a[i][j] * a[i][j] * a[i][j]);
		puts("");
	}
	return 0;
}

E. Move and Swap

有一个\(n\)点树,规定\(1\)是根,除了根以外的点每个点带一个权值.一开始有两个球:红球和蓝球同时在根节点位置上,每次红球可以走到当前点以下某个儿子上,蓝色点可以往下距离拓展一的任意一个点,在走完之后可以选择两个点是否交换.要求执行\(d\)步这个过程,每次走完了之后会使得分增加两个球所在点的权值之差的绝对值,保证所有叶子节点到根的距离是\(d\),求最大得分.

思路

先考虑贪心,不难发现这个球得分的过程就是在一层上抠两个点并且加分,最直接了当的方式就是每层抠最大点和最小点,显然第一个样例都过不去,注意到移动是有一定限制的,所以进一步考虑dp,贪心可以甩一边去了.

  • 状态:\(f[i]\)表示在走完了一个流程之后,红点到达\(i\)这个点上的得分集合中的最大值.
  • 入口:\(f[1] = 0\)
  • 转移:\(i\)这个点是红点有两种情况:要么是直接从上一个点走下来的,要么是交换来的.分别来看:首先对于直接走下来的情况,只需要知道这个点\(i\)的父节点\(p[i]\)就可以了,转移方程是\(f[i] = f[p[i]] + |a[i] - a[j]|\),式中\(j\)是与\(i\)同层的另外一个点.对于这个转移只需要知道:父节点以及本层最大的\(a[j]\)和最小的\(a[j]\)就可以了.另外一种转移是通过某个\(j\)交换而来的,转移方程是:\(f[i] = f[p[j]] + |a[j] - a[i]|\),考虑拆开绝对值:\(f[i] = max(f[p[j]] + a[j] - a[i],f[p[j]] + a[i] - a[j])\).合并同类项,维护一下最大的\(f[p[j]] - a[j]\)以及最大的\(f[p[j]] + a[j]\)就可以了.那么做法也比较显然了,先把所有点按照层拆进去,每层进行遍历维护几个值就可以了.
  • 出口:\(res = \max\{f[i]\}\).

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

const int N = 2e5+7,M = 2 * N;
int edge[M],succ[M],ver[N],idx;
int a[N],p[N];
ll f[N];
vector<int> d[N];

void add(int u,int v)
{
	edge[idx] = v;
	succ[idx] = ver[u];
	ver[u] = idx++;
}

void dfs(int u,int fa = -1,int dep = 1)
{
	p[u] = fa;d[dep].push_back(u);
	for(int i = ver[u];~i;i = succ[i])
	{
		int v = edge[i];
		if(v == fa)	continue;
		dfs(v,u,dep + 1);
	}
}

int main() 
{
	int T;scanf("%d",&T);
	while(T--)
	{
		int n,x;scanf("%d",&n);
		forn(i,1,n)	ver[i] = -1,d[i].clear(),f[i] = 0,p[i] = 0;idx = 0;
		
		forn(i,2,n)	scanf("%d",&x),add(i,x),add(x,i);
		forn(i,2,n)	scanf("%d",&a[i]);		
		
		dfs(1);
		
		ll res = 0;
		for(int _ = 2;!d[_].empty();++_)
		{
			ll maxaj = 0,minaj = 1e9,maxneg = -1e9,maxadd = -1e9;
			for(auto& i : d[_])
			{
				maxaj = max(maxaj,1ll*a[i]);minaj = min(minaj,1ll*a[i]);
				maxneg = max(maxneg,f[p[i]] - a[i]);maxadd = max(maxadd,f[p[i]] + a[i]);
			}	
			for(auto& i : d[_])
			{
				f[i] = max(f[i],f[p[i]] + max(abs(a[i] - maxaj),abs(a[i] - minaj)));
				f[i] = max(f[i],max(maxneg + a[i],maxadd - a[i]));
				res = max(res,f[i]);
			}
		}
		printf("%lld\n",res);
	}
	return 0;
}
posted @ 2021-02-13 17:22  随处可见的阿宅  阅读(272)  评论(0编辑  收藏  举报