2023牛客寒假算法基础集训营2个人补题 A B C D E F H J L
A B
题意
给一个n,两个区间,问区间内各取一个数加起来为n的对数有多少。
思路
直接说通解O(1)的做法,我们对于两个区间a,A,可以对每个区间对应的求出一个区间,使得在a区间内的任意一个数都可以在b区间内找到一个数,最终,我们只需要求出b与A的交集和B与a交集的最大值即可。
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
void solve() {
int l1, r1, l2, r2;
int res1=0,res2=0;
cin >> n >> l1 >> r1 >> l2 >> r2;
int l=n-r1,r=n-l1;
if(l<l2) {
if(r<l2) {
res1=0;
}
else if(r==l2){
res1=1;
}
else {
if(r<=r2) res1=r-l2+1;
else res1=r2-l2+1;
}
}
else if(l==l2) {
if(r==l2) res1=1;
else{
res1=min(r-l2+1,r2-l2+1);
}
}
else {
if(l<r2) {
res1=min(r2-l+1,r-l+1);
}
else if(l==r2) {
res1=1;
}
else {
res1=0;
}
}
l=n-r2,r=n-l2;
if(l<l1) {
if(r<l1) {
res2=0;
}
else if(r==l1){
res2=1;
}
else {
if(r<=r1) res2=r-l1+1;
else res2=r1-l1+1;
}
}
else if(l==l1) {
if(r==l1) res1=1;
else{
res2=min(r-l1+1,r1-l1+1);
}
}
else {
if(l<r1) {
res2=min(r1-l+1,r-l+1);
}
else if(l==r1) {
res2=1;
}
else {
res2=0;
}
}
cout<<max(res1,res2)<<"\n";
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
C
题意
给m个区间,询问是否能在给定m个区间(区间不能相同),有多少对数能够满足相加等于给定n。
思路
直接通过差分+前缀和的方式统计在m个区间内每个数出现的次数,然后再直接枚举每个数求和,最后去重时就相当于是B题,只是给定的区间是相同的,减掉之后就是答案。
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 998244353;
typedef pair<int, int> PII;
int n,m;
int a[N];
int d[N+10];
int sum[N+10];
vector<PII>v;
void solve() {
cin>>n>>m;
for(int i=1;i<=m;i++) {
int l,r;
cin>>l>>r;
v.push_back({l,r});
d[l]++;
d[r+1]--;
}
for(int i=1;i<=N;i++) {
sum[i]=sum[i-1]+d[i];
}
int res=0;
for(int i=1;i<=2e5;i++) {
res=(res+sum[i]*sum[n-i]%mod)%mod;
}
for(int i=0;i<m;i++) {
int l1=v[i].first,r1=v[i].second;
int l2=l1, r2=r1;
int res1=0,res2=0;
int l=n-r1,r=n-l1;
if(l<l2) {
if(r<l2) {
res1=0;
}
else if(r==l2){
res1=1;
}
else {
if(r<=r2) res1=r-l2+1;
else res1=r2-l2+1;
}
}
else if(l==l2) {
if(r==l2) res1=1;
else{
res1=min(r-l2+1,r2-l2+1);
}
}
else {
if(l<r2) {
res1=min(r2-l+1,r-l+1);
}
else if(l==r2) {
res1=1;
}
else {
res1=0;
}
}
l=n-r2,r=n-l2;
if(l<l1) {
if(r<l1) {
res2=0;
}
else if(r==l1){
res2=1;
}
else {
if(r<=r1) res2=r-l1+1;
else res2=r1-l1+1;
}
}
else if(l==l1) {
if(r==l1) res1=1;
else{
res2=min(r-l1+1,r1-l1+1);
}
}
else {
if(l<r1) {
res2=min(r1-l+1,r-l+1);
}
else if(l==r1) {
res2=1;
}
else {
res2=0;
}
}
res=(res+mod-max(res1,res2))%mod;
}
cout<<res<<"\n";
}
signed main() {
IOS;
int t = 1;
//cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
D
题意
给一棵树,给n个能量,每个能量只能放在一个节点上,选择一个能量,再选择一个没有能量球树节点 x,把刚刚选择的能量放置在节点 x上。在这之后,能获得以 x 为根的子树中的所有能量球的能量 (包括节点 x 的能量球能量)。在放置完所有能量球后,可能获得的总能量最多是多少?
思路
稍微看一下就会发现其实就是按照深度排序放置一定最大。
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
vector<int>g[N];
map<int,vector<int>>dep;
void dfs(int u,int d) {
dep[d].push_back(u);
for(auto v:g[u]) {
dfs(v,d+1);
}
}
void solve() {
cin>>n;
for(int i=2;i<=n;i++) {
int u,v=i;
cin>>u;
g[u].push_back(v);
}
for(int i=1;i<=n;i++) {
cin>>a[i];
}
dfs(1,1);
sort(a+1,a+n+1);
int res=0;
int id=1;
for(auto [i,_]:dep) {
for(int j=id;j<id+_.size();j++) {
//cout<<a[id]<<" "<<i<<"\n";
res+=a[j]*i;
}
id=id+_.size();
//cout<<id<<"\n";
}
cout<<res<<"\n";
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
E
题意
f ( x ) = ⌊ n x ⌋ + x − 1 f(x)=\lfloor \frac{n}{x} \rfloor+x−1 f(x)=⌊xn⌋+x−1她想在区间 [L,R] 中找到一个最小的整数 t,使函数 f(t) 的值最小。
思路
其实是一个诈骗题,因为只能取整数,函数其实就变成了 f ( x ) = ⌊ n x + x − 1 ⌋ f(x)=\lfloor \frac{n}{x} +x-1 \rfloor f(x)=⌊xn+x−1⌋,那么画图就会发现,其实极值就是在 s q r t ( n ) sqrt(n) sqrt(n)附近取,更细一点就会发现值不是向下取整就是向上取整,所以只需要讨论给定区间和最值区间的关系,最终二分就行了,训练时错的地方在讨论一类的时候忘记了其实其他位置有可能更小,不一定就是右值。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, k;
int a[N];
int f(int x) { return n / x + x - 1; }
void solve() {
int l, r;
cin >> n >> l >> r;
int mi = sqrt(1.0 * n);
if (f(mi + 1) < f(mi)) mi++;
if (mi > r) {
int ll = l, rr = r;
while (ll < rr) {
int mid = ll + rr >> 1;
if (f(mid) > f(r))
ll = mid + 1;
else
rr = mid;
}
cout << ll<< "\n";
} else if (mi < l) {
cout << l << "\n";
} else {
int ll = l, rr = mi;
while (ll < rr) {
int mid = ll + rr >> 1;
if (f(mid) > f(mi))
ll = mid + 1;
else
rr = mid;
}
cout << ll<< "\n";
}
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
F
题意
给一个地图,有些地方有障碍物,其他地方都放着一枚金币,问反复走最多可以拿到多少枚。
思路
很典的题,就只要找到从起点开始和从终点开始能到达的点有哪些,dfs有可能超时,bfs完美解决()
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n,k;
int a[N];
int g[N][5];
int dx[]={1,0};
int dy[]={0,1};
int dx1[]={-1,0};
int dy1[]={0,-1};
bool f;
void bfs1(int x,int y) {
queue<PII>q;
q.push({x,y});
while(q.size()) {
int xx=q.front().first;
int yy=q.front().second;
q.pop();
if(g[xx][yy]!=0) {
if(g[xx][yy]!=2) {
g[xx][yy]=2;
for(int i=0;i<2;i++) {
q.push({xx+dx[i],yy+dy[i]});
}
}
}
}
}
int vis[N][5];
void bfs2(int x,int y) {
queue<PII>q;
q.push({x,y});
while(q.size()) {
int xx=q.front().first;
int yy=q.front().second;
q.pop();
if(vis[xx][yy]!=0) {
if(vis[xx][yy]!=2) {
vis[xx][yy]=2;
for(int i=0;i<2;i++) {
q.push({xx+dx1[i],yy+dy1[i]});
}
}
}
}
}
void solve() {
cin>>n>>k;
f=0;
for(int i=1;i<=n;i++) {
g[i][1]=g[i][2]=g[i][3]=1;
vis[i][1]=vis[i][2]=vis[i][3]=1;
}
for(int i=1;i<=k;i++) {
int x,y;
cin>>x>>y;
g[x][y]^=1;
vis[x][y]^=1;
}
bfs1(1,1);
bfs2(n,3);
int res=0;
for(int i=1;i<=n;i++) {
for(int j=1;j<=3;j++) {
if(g[i][j]==2&&vis[i][j]==2) res++;
}
}
// cout<<"##############\n";
// for(int i=1;i<=n;i++) {
// for(int j=1;j<=3;j++) {
// cout<<g[i][j]<<" ";
// }
// cout<<"\n";
// }
// cout<<"##############\n";
// cout<<"##############\n";
// for(int i=1;i<=n;i++) {
// for(int j=1;j<=3;j++) {
// cout<<vis[i][j]<<" ";
// }
// cout<<"\n";
// }
// cout<<"##############\n";
cout<<max(0LL,res-1)<<"\n";
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
H
题意
有一个长度为 n 的序列 a,把这个序列划分成 k 个非空子序列。定义序列的值为这个序列中只出现一次的数字的个数。对于 k = 1 … n k=1 \ldots n k=1…n,把序列 a 划分成 k 个非空子序列后,所有子序列的值的和最大是多少。
思路
统计一下每个数字出现的次数,然后排个序,先写个暴力程序就会发现,当分成i段的时候出现次数小于i的贡献为i-1,否则就是出现的次数,故可以直接用二分做复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
int sum[N];
void solve() {
cin>>n;
map<int,int>mp;
for(int i=1;i<=n;i++) {
cin>>a[i];
mp[a[i]]++;
}
vector<int>v;
for(auto [i,j]:mp) {
v.push_back(j);
}
sort(v.begin(),v.end());
for(int i=1;i<=v.size();i++) {
sum[i]=sum[i-1]+v[i-1];
}
vector<int>res;
res.resize(n+10);
//for(int i=mp.size();i<=n;i++) res[i]=n;
for(int i=1;i<=n;i++) {
// for(auto j:v) {
// if(i>=j)res[i]+=j;
// else {
// res[i]+=i-1;
// }
// }
auto k=upper_bound(v.begin(),v.end(),i);
//cout<<v.end()-k<<' '<<k-v.begin()<<"*********************\n";
res[i]=(i-1)*(v.end()-k)+sum[k-v.begin()]-sum[0];
//cout<<sum[1]<<" "<<sum[2]<<"\n";
}
for(int i=1;i<=n;i++) cout<<res[i]<<"\n";
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
J
题意
定义 M x A b ( i , j ) = m a x ( ∣ a i − a j ∣ , ∣ a i + a j ∣ ) MxAb(i,j)=max(∣ai−aj∣,∣ai+aj∣) MxAb(i,j)=max(∣ai−aj∣,∣ai+aj∣),求下列式子 ∑ i = 1 n ∑ j = 1 n M x A b ( i , j ) \sum_{i=1}^{n} \sum_{j=1}^{n} MxAb(i,j) ∑i=1n∑j=1nMxAb(i,j)
思路
稍微分类讨论一下就发现其实是一个固定结论,答案就是 2 n ˙ ∑ ˙ i = 1 n ∣ a i ∣ 2 \dot n \dot \sum_ {i=1}^n |ai| 2n˙∑˙i=1n∣ai∣
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
void solve() {
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int res=0;
for(int i=1;i<=n;i++) {
res+=2*n*abs(a[i]);
}
cout<<res<<"\n";
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
L
题意
有一个长度为 n 的序列 a 和一个正整数 p 。
对于所有 x = 0 … p − 1 x=0 \ldots p-1 x=0…p−1,求三元组 [i,j,k] 的数量,满足:
-
** i ≠ j i \ne j i=j and i ≠ k i \ne k i=k and j ≠ k j \ne k j=k **
-
** ( a i ⋅ a j + a k ) ≡ x (mod p ) (a_i \cdot a_j + a_k) \equiv x \text{ (mod } p) (ai⋅aj+ak)≡x (mod p) **
思路
预处理一下所有 a i a ˙ j a_i \dot a_j aia˙j模p的数字,然后统计一下,最终再根据这个去枚举k的值,最后去个重就结束了。
代码
#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n,m;
int a[5050];
int num[5050];
int res[5050];
void solve() {
int p;
cin>>n>>p;
for(int i=1;i<=n;i++) {
cin>>a[i];
a[i]=a[i]%p;
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
if(i!=j) {
num[a[i]*a[j]%p]++;
res[(a[i]*a[j]%p+a[i])%p]--;
res[(a[i]*a[j]%p+a[j])%p]--;
}
}
}
for(int i=0;i<p;i++) {
for(int j=1;j<=n;j++) {
res[(i+a[j])%p]+=num[i];
}
}
for(int i=0;i<p;i++) assert(res[i]>=0);
for(int i=0;i<p;i++) cout<<res[i]<<" \n"[i==p-1];
}
signed main() {
IOS;
int t = 1;
//cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
K
题意
给一个图,有q次询问,每次给一个点集,问点集中有多少条边是存在于给定图中。
思路
难点在于想到复杂度是 n s q r t ( n ) nsqrt(n) nsqrt(n),想到之后就可以发现就可以用根号分治来分类讨论,或者直接将无向图根据度数大小关系重新变成一个有向图,暴力即可,两种方法本质其实一样,难的在于想到根据 s q r t ( n ) sqrt(n) sqrt(n)分类讨论。
代码
根号分治
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, m, q;
int a[N];
vector<int> g[N], g1[N];
vector<PII> ed;
vector<int> s(N);
void solve() {
cin >> n >> m >> q;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
ed.push_back({u, v});
g[v].push_back(u);
g[u].push_back(v);
}
int st = sqrt(n);
for (int i = 0; i < m; i++) {
int u = ed[i].fi, v = ed[i].sc;
if (g[u].size() > st && g[v].size() > st) {
g1[u].push_back(v);
g1[v].push_back(u);
}
}
while (q--) {
int k;
cin >> k;
int ans = 0;
vector<int> v[2];
for (int i = 1; i <= k; i++) {
int u;
cin >> u;
s[u] = 1;
if (g[u].size() > st)
v[1].push_back(u);
else
v[0].push_back(u);
}
for (auto u : v[0]) {
for (auto x : g[u]) {
if (s[x]) {
if (g[x].size() <= st) {
if (x > u) ans++;
} else
ans++;
}
}
}
for (auto u : v[1]) {
for (auto x : g1[u]) {
if (s[x]) if (x > u) ans++;
}
}
for (auto u : v[0]) {
s[u] = 0;
}
for (auto u : v[1]) {
s[u] = 0;
}
cout << ans << "\n";
}
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
重新建图
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n, m, q;
int a[N];
vector<int> g[N];
int d[N];
vector<PII> ed;
vector<int> st(N);
void solve() {
cin >> n >> m >> q;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
ed.push_back({u, v});
d[u]++;
d[v]++;
}
for (int i = 0; i < m; i++) {
int u = ed[i].fi, v = ed[i].sc;
if (d[u] > d[v])
g[v].push_back(u);
else
g[u].push_back(v);
}
while (q--) {
int k;
cin >> k;
int ans = 0;
vector<int> v;
for (int i = 1; i <= k; i++) {
int u;
cin >> u;
st[u] = 1;
v.push_back(u);
}
for (int i = 0; i < k; i++) {
for (auto j : g[v[i]]) {
if (st[j]) ans++;
}
}
for (auto u : v) {
st[u] = 0;
}
cout << ans << "\n";
}
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}