7.18小测
预计估分:\(100+100+100+100=400\)。
实际得分:\(100+100+100+100=400\)。继续加油。
I. 区间求和
I.I 题目描述
给定一个长度为 \(n\) 的正整数序列 \(a_1,a_2,\dots,a_n\),和一个整数 \(m\),求有多少对 \(l,r(l\le r)\) 使 \(\displaystyle\sum_{i=l}^{r}{a_i}\) 为 \(m\)。
I.II 题解
模板题,正整数序列前缀和有单调性,考虑二分。
I.III 代码
/*+ Cloudybunny +*/
#include<bits/stdc++.h>
#define endl '\n'
#define pi pair<int,int>
#define int long long
using namespace std;
const int INF=INT_MAX;
const int mod=1e9+7;
const int N=1e5+10;
int n,m,a[N],s[N];
inline int read(){
int x;
cin>>x;
return x;
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;i++)
s[i]=s[i-1]+(a[i]=read());
int ans=0;
for(int i=1;i<=n;i++){
int l=0,r=i-1,res=-1;
while(l<=r){
int mid=(l+r)>>1;
if(s[i]-s[mid]>=m) l=mid+1,res=mid;
else r=mid-1;
}
if(s[i]-s[res]==m) ans++;
}
cout<<ans<<endl;
return 0;
}
II. 社交网络服务
II.I 题目描述
有 \(n\) 个用户,分别用从 \(1\sim n\) 的数字标记,有 \(m\) 对朋友关系,第 \(i\) 对由用户 \(a_i\) 和 \(b_i\) 组成,两人互为朋友。
确定以下操作可以执行的最大次数:选择三个不同用户 \(x,y,z\),使得 \(x\) 和 \(y\) 是朋友,\(y\) 和 \(z\) 是朋友,但 \(x\) 和 \(z\) 不是朋友,那么让 \(x\) 和 \(z\) 成为朋友。
II.II 思路
我们知道,如果每对朋友之间连线,则 \(n\) 个人会组成很多张图。易知每张图中的人不能与其他图的人成为朋友,因为 \(x,z\) 中没有 \(y\),就是所谓的“桥梁”。那我们只需要每张图分析。
当一张图的所有人都成为朋友(直系朋友,就是有连线的),那么就实现了单张图的操作次数最大化。而这种形态的图,就是完全图。
所以我们要将每一个图变成完全图,当点数为 \(n\) 是,完全图边数为 \(\frac{n\times (n-1)}2\)。
因为要统计哪些点在一张图中,所以初步考虑使用并查集维护。
每次合并集合(图)时,同时维护该集合(图)的祖先结点和集合(图)点的数量与边的数量。
II.III 代码
/*+ Cloudybunny +*/
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define pi pair<int,int>
using namespace std;
const int INF=INT_MAX;
const int mod=1e9+7;
const int N=2e5+10;
int n,m,fa[N],siz[N],cnt[N];
inline int read(){
int x;
cin>>x;
return x;
}
int find(int x){
return (x==fa[x]?x:fa[x]=find(fa[x]));
}
inline void merge(int u,int v){
int fu=find(u),fv=find(v);
if(fu!=fv){
fa[fu]=fv;
siz[fv]+=siz[fu];
cnt[fv]+=cnt[fu]+1;
}else cnt[fu]++;
return ;
}
inline void init(){
for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
return ;
}
signed main(){
n=read(),m=read();
init();
for(int i=1;i<=m;i++){
int u=read(),v=read();
merge(u,v);
}
int ans=0;
for(int i=1;i<=n;i++)
if(find(i)==i)
ans+=(siz[i]*(siz[i]-1)/2-cnt[i]);
cout<<ans<<endl;
return 0;
}
III. 乱来
III.I 题目描述
给出 \(n(1\le n\le10^5)\) 个数的数组 \(a\) 和一个正整数 \(k(1\le k\le10^9)\)。
共进行 \(k\) 次操作,每次操作选择一个下标 \(i\in[1,n]\),让 \(a_i\) 变成 \(2\times a_i\)。
请问 \(k\) 次操作以后 \(a_1+ a_2+...+ a_n\) 的值最小可以是多少,由于答案可能很大,你需要输出答案对 \(10^9+7\) 取模。
III.II 题解
一个显而易见的贪心思路:每次让最小的数乘以 \(2\),用优先队列。
发现 \(k\) 太大了,不能直接暴力。考虑只操作一定次数,但不会超时,同时数组升序。然后整个数组同时扩倍(乘以 \(2\)),一次性操作 \(k\) 次,不改变数列大小关系,贪心策略依然可用。最后不足 \(k\) 次,因为数组升序,贪心可用,依次乘以 \(2\) 即可。
III.III 代码
/*+ Cloudybunny +*/
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define pi pair<int,int>
using namespace std;
const int INF=INT_MAX;
const int mod=1e9+7;
const int N=2e5+10;
int n,k,a[N];
priority_queue<int,vector<int>,greater<int> > q;
inline int read(){
int x;
cin>>x;
return x;
}
inline int qpow(int a,int b){
int res=1;
while(b){
if(b&1) res=(res*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return res;
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
n=read(),k=read();
for(int i=1;i<=n;i++) q.push(read());
while(k&&q.top()<(1<<29)){
k--;
int t=q.top();
q.pop();
q.push(t*2);
}
for(int i=1;i<=n;i++){
a[i]=q.top();
q.pop();
}
int ans=0,bit=qpow(2,k/n);
for(int i=1;i<=n;i++){
if(i<=k%n) ans=(ans+2*a[i]%mod)%mod;
else ans=(ans+a[i])%mod;
}
cout<<ans*bit%mod;
return 0;
}
IV. 浏览计划
IV.I 题目描述
有 \(n(1\le n\le4000)\) 个点,\(m(1\le m\le5000)\) 条双向边。求所有数对 \((a,b,c,d)\) ,从 \(a\to b\to c\to d\) 走最短路的经过道路数量最多是多少?
IV.II 题解
首先,每个点到其他点的最短路是要求出来的。只不过不用全源最短路,\(n\) 次 bfs 也是可以接受的。
我们容易想到直接枚举四个点,时间复杂度 \(\mathcal{O}(N^4)\),不能接受。
但其实可以用一点贪心思想:为了使路程最长,一旦选定一个点 \(x\),那肯定是到达离这个点最远的点最优。所以,其实只需要枚举两个点 \(x,y\),分两种情况:\(x的最远点\to x\to y\to y的最远点\) 或 \(y的最远点\to y\to x\to x的最远点\)。
IV.III 代码
/*+ Cloudybunny +*/
#include<bits/stdc++.h>
#define endl '\n'
#define pi pair<int,int>
using namespace std;
const int INF=INT_MAX;
const int mod=1e9+7;
const int N=4e3+10;
int n,m;
vector<int> a[N];
int d[N][N];
bool vis[N];
pi ma[N][3];
inline int read(){
int x;
cin>>x;
return x;
}
inline void bfs(int op){
queue<int> q;
d[op][op] = 1;
q.push(op);
while (!q.empty()) {
int x = q.front();
q.pop();
for (int i = 0; i < a[x].size(); i++) {
int to = a[x][i];
if (!d[op][to]) {
d[op][to] = d[op][x] + 1;
q.push(to);
}
}
}
ma[op][0] = ma[op][1] = ma[op][2] = make_pair(0, 0);
for (int j = 1; j <= n; j++) {
if (d[op][j] > ma[op][0].first)
ma[op][2] = ma[op][1], ma[op][1] = ma[op][0], ma[op][0] = make_pair(d[op][j], j);
else if (d[op][j] > ma[op][1].first)
ma[op][2] = ma[op][1], ma[op][1] = make_pair(d[op][j], j);
else if (d[op][j] > ma[op][2].first) ma[op][2] = make_pair(d[op][j], j);
}
return ;
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
n=read(),m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read();
a[u].push_back(v);
a[v].push_back(u);
}
for (int i = 1; i <= n; i++) bfs(i);
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
vis[i] = vis[j] = true;
// i的最远点 -> i -> j -> j的最远点
int t = -1;
for (int k = 0; k < 3; k++) {
if (!vis[ma[i][k].second]) {
t = k;
vis[ma[i][k].second] = true;
break;
}
}
if (t != -1) {
for (int k = 0; k < 3; k++) {
if (!vis[ma[j][k].second]) {
ans = max(ans, ma[i][t].first -1 + d[i][j] -1 + ma[j][k].first -1);
break;
}
}
vis[ma[i][t].second] = false;
}
// j的最远点 -> j -> i -> i的最远点
t = -1;
for (int k = 0; k < 3; k++) {
if (!vis[ma[j][k].second]) {
t = k;
vis[ma[j][k].second] = true;
break;
}
}
if (t != -1) {
for (int k = 0; k < 3; k++) {
if (!vis[ma[i][k].second]) {
ans = max(ans, ma[j][t].first -1 + d[i][j] -1 + ma[i][k].first -1);
break;
}
}
vis[ma[j][t].second] = false;
}
vis[i] = vis[j] = false;
}
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号