作业介绍

2026年编程兔冬令营集训第二场作业

1、前缀和 2、最短路径

1、一维前缀和定义b[i]=sum(a[1]~a[i]),则有sum(a[l]~a[r]) = b[r]-b[l-1]
#include<bits/stdc++.h>
using namespace std;
long long a[100005], sum[100005];
int main(){
	int n,q,l,r;
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		sum[i]= sum[i-1]+a[i];//求前缀和
	}
	cin>>q;
	while(q-->0){
		cin>>l>>r;
		cout<<sum[r]-sum[l-1]<<endl;//快速求区间和
	}
	return 0;
}

2、二维前缀和定义:构建前缀和数组:对于二维矩阵a,定义前缀和数组sum[i][j]表示从矩阵左上角(1,1)到位置(i,j)的矩形区域内所有元素的和,其递推公式为:
sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j]

查询子矩阵和:若要查询以(x1,y1)为左上角、(x2,y2)为右下角的子矩阵的元素和,公式为:
sum(x2,y2,x1,y1) = sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1]


#include<bits/stdc++.h>
using namespace std;
long long a[1005][1005], sum[1005][1005];
int main(){
	int n,m,q,x1,y1,x2,y2;
	scanf("%d%d%d",&n,&m,&q); //输入较多,使用scanf 
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			scanf("%lld",&a[i][j]);
			sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j];
		}
	}

	while(q-->0){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		long long s =sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1];
		printf("%lld\n",s);//输出较多,使用printf 
	}
	return 0;
}

3、异或性质 a^a =0,a^a^b=b,异或前缀和定sum[i]=a[1]…^…a[i]),则有xor(a[l]~a[r]) =sum[r]^sum[l-1] 
#include<bits/stdc++.h>
using namespace std;
long long a[5005];
int main(){
	long long i,j,n,ans=0;
	cin>>n;
	for(i=1;i<=n;++i) {
		cin>>a[i];
		a[i] = a[i]^a[i-1];//异或前缀和
	}
	for(i=1;i<=n;++i)
		for(j=i;j<=n;++j){
			ans=max(ans, a[j]^a[i-1]);//枚举i~j,快速求区间异或和
		}
	cout<<ans<<endl;
	return 0;
}
4、异或性质 a^a =0,a^a^b=b;a^b=c相当于a=c^b异或前缀和定sum[i]=a[1]…^…a[i]),则有xor(a[l]~a[r]) =sum[r]^sum[l-1],要使得xor(a[l]~a[r]) =sum[r]^sum[l-1]=k,则需要sum[l-1]^k=sum[r],也就是sum[l-1]=sum[r]^k,对于每一个r只需要找到最靠近的l就可。
同时应用到动态规划,假设dp[i]表示从第一个数到第i个数可以划分的区间数量的最大值。则有dp[r] = max(dp[r-1],dp[l-1]+1)。
#include<bits/stdc++.h>
using namespace std;
long long a[500005];
long long dp[500005];
map<long long,long long> mp;//记录sum[i]与i的映射关系
int main(){
	long long i,j,n,k,t,ans=0;
	cin>>n>>k;
	dp[0] = 0;
	mp[0] = 0;
	for(i=1;i<=n;++i) {
		cin>>a[i];
		a[i] = a[i]^a[i-1];//前缀异或和
		t= a[i]^k;//t=sum[l-1]=sum[r]^k
		dp[i] = dp[i-1];
		if (mp.find(t)!=mp.end()){//找到最靠近的j就可
			j=mp[t];
			dp[i] = max(dp[i], dp[j]+1); 
		}
		mp[a[i]] = i;//更新前缀和映射
		ans = max(ans, dp[i]);
	}
	
	cout<<ans<<endl;
	return 0;
}

5、单点到多点的最短距离,dijkstra 算法处理带非负权图,使用优先队列处理priority_queue,贪心思想每次扩展总距离最短的点。
#include<bits/stdc++.h>
using namespace std;
priority_queue<pair<long long, long long>> pq;//从大到小排序,可以取负数(从小到大)
vector<pair<long long, long long>> vc[100005];//边权图
pair<long long, long long>  p;
long long dis[100005];//出发点到每个点的最短距离
int main(){
	long long i,j,n,m,s,k,t,ans=0,vi,ui,wi;
	//cin>>n>>m>>s;
	scanf("%lld%lld%lld",&n,&m,&s);
	memset(dis,-1,sizeof(dis));
	dis[s] =0;
	//建立边的关系,注意是有向边 
	for(i=0;i<m;++i){
		//cin>>vi>>ui>>wi;
		scanf("%lld%lld%lld",&vi,&ui,&wi);
		vc[vi].push_back(make_pair(ui,wi));
		//vc[ui].push_back(make_pair(vi,wi));
	}
	pq.push(make_pair(0,s));
	while(pq.size()>0){//dijkstra 算法模板
		p = pq.top(); wi=-p.first;vi=p.second;
		pq.pop();
		//选出当前剩余中距离最短的点来进行扩展 
		if (wi==dis[vi]){
			for(i=0;i<vc[vi].size();++i){
				p = vc[vi][i]; ui=p.first; wi = p.second;
				if (dis[ui] == -1 || dis[ui]> dis[vi]+wi){
					dis[ui] = dis[vi]+wi;
					pq.push(make_pair(-dis[ui],ui));
				}
			}
		}
	}
	for(i=1;i<=n;++i) printf("%lld ",dis[i]); 
	printf("\n");
	return 0;
}


6、不论经过多少轮交换,交换的总价是Va-Vb+交换次数(思考一下为什么?)。所以交换次数越少越好,这时候可以使用队列queue来替换优先队列处理priority_queue(思考一下为什么?)。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n,m,s,t,v[N];
vector<int> e[N];
queue<pair<int,int> > q;//交换次数,每次权值都为1
bool vis[N];
int main() {
	cin >> n >> m >> s >> t;
	for(int i = 0;i<n;i++) cin >> v[i];
	for(int i = 0;i<m;i++) {
		int u,v;
		cin >> u >> v;
		e[u].push_back(v);
	}
	q.push({s,0});
	while(!q.empty()) {
		int u = q.front().first;
		int st = q.front().second;
		q.pop();
		if(u == t) {
			cout << st + v[t] - v[s];
			return 0;
		}
		for(auto v : e[u]) {
			if(!vis[v]) {
				vis[v] = true;
				q.push({v,st+1});
			}
		}
	}
	cout << "No solution";
	return 0;
}
7、由于离开时间必须是 k 的整数倍,可以将时间对 k 取模,将问题转化为 分层图最短路径问题。定义状态 (r, u),表示在模 k 余数为 r 的时刻到达节点 u 的最早时间。
对于每条边 (u, v, a),假设当前在 u 的时刻为 T(模 k 余数为 cur),需要等待到不小于 a 且模 k 余数为 (cur+1) mod k 的时刻才能通过这条边。到达 v 的时刻为 T + 1 + 等待时间。

#include <bits/stdc++.h>
using namespace std;

// 定义节点结构体,用于优先队列
struct Node {
    int v;    // 节点编号
    int w;    // 到达该节点的实际时间
    int k;    // 时间模 k 的余数
    // 重载 < 运算符,用于优先队列(小顶堆)
    bool operator <(const Node &b) const {
        return w > b.w; // 注意:这里反向定义,使得实际时间小的优先级高
    } 
};

const int N = 1e4 + 5;     // 最大节点数
const int INF = 0x3f3f3f3f; // 无穷大
const int K = 110;         // k 的最大值(100+10)
vector<Node> g[N];         // 邻接表存图,每个元素是 {v, a_i}(这里用Node但只用了v和w,注意w是开放时间)
int dis[K][N];             // dis[i][j]:在模k余i的时刻到达节点j的最早时间
bool vis[K][N];            // 标记数组,记录状态是否已访问

void Dijkstra(int k) {
    priority_queue<Node> pq; // 优先队列(小顶堆)
    pq.push({1, 0, 0});      // 初始状态:在余数0时刻到达节点1,实际时间为0
    memset(dis, 0x3f, sizeof(dis)); // 初始化距离为无穷大
    dis[0][1] = 0;                 // 初始状态距离为0

    while (!pq.empty()) {
        int cur = pq.top().k;      // 当前状态的余数
        int u = pq.top().v;        // 当前节点
        pq.pop();
        if (vis[cur][u]) continue; // 如果已访问,跳过
        vis[cur][u] = true;        // 标记为已访问

        // 遍历从u出发的所有边
        for (int i = 0; i < g[u].size(); i++) {
            int v = g[u][i].v;     // 邻居节点
            int a = g[u][i].w;     // 该边的开放时间
            int next = (cur + 1) % k; // 下一步的余数(因为通过边需要1单位时间)

            int t = dis[cur][u];   // 当前实际时间
            // 如果当前时间t小于开放时间a,需要等待
            if (t < a) {
                // 计算需要等待的k的倍数:ceil((a - t) / k)
                int wait = (a - t + k - 1) / k * k;
                t += wait;
            }
            // 通过边后,到达v的时间为 t+1
            if (dis[next][v] > t + 1) {
                dis[next][v] = t + 1;
                pq.push({v, t+1, next});
            }
        }
    }
}

int main() {
	freopen("bus.in","r",stdin);
	freopen("bus.out", "w",stdout);
    int n, m, k;
    cin >> n >> m >> k;
    while (m--) {
        int u, v, w;
        cin >> u >> v >> w;
        // 将边加入邻接表:从u到v,开放时间为w
        g[u].push_back({v, w}); // 这里Node结构体的k字段未使用(默认为0)
    }
    Dijkstra(k);
    // 输出答案:在余数0时刻到达n的最早时间
    if (dis[0][n] == INF) cout << -1;
    else cout << dis[0][n];
    return 0;
}


题目

认领作业后才可以查看作业内容。
状态
正在进行…
题目
7
开始时间
2026-2-2 0:00
截止时间
2027-1-1 23:59
可延期
24 小时