大佬说补题要写题解,于是有了这篇文章。
题目链接:这里
A
A题太无聊了,不想写。
无聊你还卡二十分钟
B
题意:给定一个序列,可以任意排序,求新构造的序列最小的。
首先,如果序列中全是0,值一定是1。
我们考虑0的个数为,考虑1的个数为,考虑序列的长度为。
1.序列非0的个数为,如果,即非0的元素可以把所有0元素隔开,则为0
2.否则不为0,我们直接把所有非0的放在一起,两头放上较大的。
3.如果,非0即1,序列中会有0,1,则为2,否则mex为1。
C
题意:给定一个长度为的序列,你需要构造另一个序列,满足所有长度为n的子序列的区间积=其他位置的区间和,你需要构造最小的序列。
如果中每个元素都相等,设该元素为,有,即,可以解得在或或时成立,所以这些情况记得特判。
否则假设不全相等。考虑长度为的序列,,
两式做减法(建议推的时候可以展开写写),我们会得到,做若干次这样的迭代,我们可以发现所有长度为(n-1)的段的乘积必须是-1,那么取从2到的每个值都为-1,注意,如果n为奇数,n-1为偶数则不满足该条件。接下来,考虑和的大小。,,解得。任取一个不含的区间,有,解得。
接下来计算贡献,对分别排序排序即可。
代码写的有点丑,还是不贴了,注意特判就行。
D
看了看,感觉很难,暂时不打算补
E
题意:给定一棵树,若干次询问,每次给定两个深度相同的结点(x,y),每次另,,并累加的贡献。其中是的父亲结点。
考虑暴力的解法,直接爆搜,这简直T飞了!
考虑优化,记忆化搜索。
本题数据范围是,不妨分析下复杂度。
考虑当前层的深度的结点个数为x,若,则被调用的次数是,这样的深度有个,复杂度为=
若 这样的层数 最多有q次这样的询问,不采取记忆化,复杂度为
总复杂度为
// 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;
}