2023牛客寒假算法基础集训营2个人补题 A B C D E F H J L K

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+x1她想在区间 [L,R] 中找到一个最小的整数 t,使函数 f(t) 的值最小。

思路

其实是一个诈骗题,因为只能取整数,函数其实就变成了 f ( x ) = ⌊ n x + x − 1 ⌋ f(x)=\lfloor \frac{n}{x} +x-1 \rfloor f(x)=xn+x1,那么画图就会发现,其实极值就是在 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=1n,把序列 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(aiaj,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=1nj=1nMxAb(i,j)

思路

稍微分类讨论一下就发现其实是一个固定结论,答案就是 2 n ˙ ∑ ˙ i = 1 n ∣ a i ∣ 2 \dot n \dot \sum_ {i=1}^n |ai| 2n˙˙i=1nai

代码

#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=0p1,求三元组 [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) (aiaj+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();
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值