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;
}
posted @ 2025-07-20 20:32  酱云兔  阅读(32)  评论(0)    收藏  举报