第一题和第二题只是数据不同
第一题可以通过a+b=n 不枚举第二个区间 直接把b算成n-a就能过
第二题只要有枚举那么必超时 所以肯定是推式子
可知答案是取[L2,R2] [n-R1,n-L1]的交集
求交集有固定写法
这个交集就是[max(L2,n-R1),min(R2,n-L1)]
当然可能没有交集 所以最后要和0max一下

[a+b=n] [c++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<bits/stdc++.h>
using namespace std;
int n,l1,r1,l2,r2,a;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int a;
cin>>a;
while(a--)
{
int tot=0;
cin>>n;
cin>>l1>>r1;
cin>>l2>>r2;
l1=max(l1,n-r2);
r1=min(r1,n-l2);
tot=max(0,r1-l1+1);
cout<<tot<<endl;
}
return 0;
}

j题是个奇怪的思维题 结论就是2n*(abs(a1)+abs(a2)+….+abs(an))
是通过数轴上分类讨论知道的

[j] [c++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<bits/stdc++.h>
#define LL long long

using namespace std;

void work()
{
LL n;
cin>>n;
LL ans=0;
for(int i=1;i<=n;i++)
{
int m;
cin>>m;
ans+=n*std::abs(m);

}
cout<<ans*2<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
{
work();
}
return 0;
}

H题 萌新必学题?
集训2最妙的一道题

单独考虑算贡献

不同种类数之间单独考虑如1111223
若2 3已放只考虑1的放法 2和3对答案的贡献是不会变的

那么我们假设只考虑1 1出现了x次 要分成k个子序列
贡献最大的情况便可以分类讨论一下

就是x>k时 前k-1个子序列都放一个1 剩下的都放到最后一个子序列 这样就贡献了k-1
若x<=k 那么每个子序列放一个1就行了 总共为答案贡献了x

知道以上信息 若k是定值 就可以做了
但是题目要求输出k为1到n的所有情况 所以还需要特殊处理

若从k=n开始减 若k一直大于等于最大的每个数出现的个数x(某个)时 贡献就是每个数的x相加 这不会变
变化的情况就是k=x(max)-1
那么我们用一个set集合存这个变化的贡献

[h] [c++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<bits/stdc++.h>
using namespace std;
int T,n,a[100010],c[100010];
int main()
{
cin>>T;
while(T--)
{
cin>>n;
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
{
cin>>a[i];
c[a[i]]++;
}
multiset<int>st; //不会去掉重复元素的集合 存每种数的x(出现次数)
for(int i=1;i<=100000;i++)
{
st.insert(c[i]);
}

long long lesum=0,ge=((int)st.size());
// ge存有多少种数的出现次数大于k
for(int i=1;i<=n;i++)
{
while(!st.empty()&&*st.begin()<=i)
{
lesum+=(long long)(*st.begin());
// 若这个数的出现次数小于等于k 那么这个数的贡献就是出现次数
st.erase(st.begin());
ge--; //
}
cout<<lesum+ge*(i-1)<<endl; //对于这个k 总贡献就是lesum与剩下的数的贡献之和 剩下的这些数的出现次数都大于k 所以每种数的贡献都是k-1

}
}
return 0;
}

d题是一个树的dfs
是用到了一个经典但我没见过的结论 两个向量内部允许任意重排 点积最大为升序排列

也就是深度序列由小向大排列 能量值序列由小向大排列 相乘相加

那么剩下就是如何求每个节点的深度了 便用dfs

[h] [c++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<bits/stdc++.h>
using namespace std;
int n,fa[200010];
vector<int> g[200010];
long long v[200010],dep[200010];
void dfs(int now,int d) //求每个节点的depth
{
dep[now]=d;
for(auto to:g[now])
{
dfs(to,d+1);
}
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=2;i<=n;i++)
{
cin>>fa[i];
g[fa[i]].push_back(i);
}
for(int i=1;i<=n;i++) cin>>v[i];
dfs(1,1);
sort(v+1,v+1+n);
sort(dep+1,dep+1+n);
long long ans=0;
for(int i=1;i<=n;i++)
{
ans+=dep[i]*v[i];
}
cout<<ans<<endl;
return 0;
}