Codeforces round 858(div 2)题解 个人小结 B.D.E

大佬说补题要写题解,于是有了这篇文章。
题目链接:这里

A

A题太无聊了,不想写。

无聊你还卡二十分钟

B

题意:给定一个序列,可以任意排序,求新构造的序列a_i+a_{i+1}最小的mex

首先,如果序列中全是0,mex值一定是1。

我们考虑0的个数为cnt_0,考虑1的个数为cnt_1,考虑序列的长度为n


1.序列非0的个数为n-cnt_0,如果n-cnt_0-1\geq cnt0,即非0的元素可以把所有0元素隔开,则mex为0

2.否则mex不为0,我们直接把所有非0的放在一起,两头放上较大的。

3.如果n-cnt_0=cnt_1,非0即1,序列a_i+a_{i+1}中会有0,1,则mex为2,否则mex为1。

C

题意:给定一个长度为2n的序列a_{2n},你需要构造另一个序列b_{2n},满足所有长度为n的子序列的区间积=其他位置的区间和,你需要构造\sum_{i=1}^{2n}|a_i-b_i|最小的序列。

如果b_i中每个元素都相等,设该元素为x,有x^n=n*x,即x^{n-1}=n,可以解得在n=1n=2,x=2x=0时成立,所以这些情况记得特判。
否则假设不全相等。考虑长度为2n的序列b_i,\sum_{i=1}^nb_i=\prod _{i=n+1}^{2n}b_i,\sum_{i=2}^{n+1}b_i=b_1\prod _{i=n+2}^{2n}b_i
两式做减法(建议推的时候可以展开写写),我们会得到\prod _{i=2}^{n}b_i=-1,做若干次这样的迭代,我们可以发现所有长度为(n-1)的段的乘积必须是-1,那么取b_i从2到2n-1的每个值都为-1,注意,如果n为奇数,n-1为偶数则不满足该条件。接下来,考虑b_1b_{2n}的大小。\sum_{i=1}^nb_i=\prod _{i=n+1}^{2n}b_i(-1)^{n-1}b_1=b_{2n}-(n-1),解得b_1+b_{2n}=n-1。任取一个不含b_1,b_{2n}的区间,有(-1)^{n-2}b_1b_{2n}=-n,解得b_1=-1,b_{2n}=n

接下来计算贡献,对a,b分别排序排序即可。 
代码写的有点丑,还是不贴了,注意特判就行。

D

看了看,感觉很难,暂时不打算补

E

题意:给定一棵树,若干次询问,每次给定两个深度相同的结点(x,y),每次另x->fa_x,y->fa_y,并累加a_xa_y的贡献。其中fa_xx的父亲结点。
考虑暴力的解法,直接爆搜,这简直T飞了!
考虑优化,记忆化搜索。
本题数据范围是1e5,不妨分析下复杂度。
考虑当前层的深度的结点个数为x,若x\leq \sqrt{n},则被调用的次数是\binom{x}{2},这样的深度有\left \lfloor \frac{n}{x} \right \rfloor个,复杂度为\left \lfloor \frac{n}{x} \right \rfloor x^2=nx\leq n\sqrt n

x>\sqrt n 这样的层数\left \lfloor \frac{n}{x} \right \rfloor< \sqrt n 最多有q次这样的询问,不采取记忆化,复杂度为q\sqrt n

总复杂度为O((n+q)\sqrt n)
 

// LUOGU_RID: 148653107
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<bitset>
#include<vector>
#define endl '\n'
#define int long long 
using namespace std;
const int N=1e5+10;
int T,n,k,m,q,a[N],fa[N],dep[N],sz[N],c[N],p[N];//dep:深度 sz:某一层的大小 p:记录当前结点在当前层的编号
vector<int>e[N];
int f[N][510];//x所在这层的结点的某个编号记录的答案
inline int read(){
	char ch = getchar();
	int x = 0, f = 1;
	while(ch < '0' || ch > '9'){
		if(ch == '-') f = -1;
		ch = getchar();
	}
	while('0' <= ch && ch <= '9'){
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}
inline void write(int x){
	if (x < 0) x = ~x + 1, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
void dfs(int u,int f){
	dep[u]=dep[f]+1;
	for(int v:e[u]){
		dfs(v,u);
	}
}
int ans(int u,int v){
	if(u==0)return 0;
	if(c[dep[u]]<=500){
		if(f[u][p[v]])return f[u][p[v]];
		return f[u][p[v]]=ans(fa[u],fa[v])+a[u]*a[v];
	}
	int res=ans(fa[u],fa[v])+a[u]*a[v];
	return res;
}
void solve(){
    n=read();
	q=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=2;i<=n;i++){
		fa[i]=read();
		e[fa[i]].push_back(i);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++){
		p[i]=++c[dep[i]];
		sz[dep[i]]++;
	}
	while(q--){
		int u,v;
		u=read();
		v=read();
		cout<<ans(u,v)<<endl;
	}
}
signed main(){
    //cin>>T;
    //while(T--)
    solve();
    return 0;
}

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值