实例 —— 操作格子
题目背景
有n个格子,从左到右放成一排,编号为1-n。
共有m次操作,有3种操作类型:
- 修改一个格子的权值,
- 求连续一段格子权值和,
- 求连续一段格子的最大值。
对于每个2、3操作输出你所求出的结果。
输入输出格式
输入格式
第一行2个整数n,m。
接下来一行n个整数表示n个格子的初始权值。
接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。
输出格式
有若干行,行数等于p=2或3的操作总数。
每行1个整数,对应了每个p=2或3操作的结果。
输入输出样例
输入样例
4 3 1 2 3 4 2 1 3 1 4 3 3 1 4
输出样例
6 3
说明 / 提示
- 对于20%的数据n <= 100,m <= 200。
- 对于50%的数据n <= 5000,m <= 5000。
- 对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。
参考代码
#include <stdio.h>
#define left (2 * i)
#define right (2 * i + 1)
#define mid ((a[i].l + a[i].r) / 2)
#define max(a, b) (a)>(b)?(a):(b)
struct node{
int l, r, sum, max;
}a[1000000];
void init(int x, int y, int i)
{
a[i].l = x;
a[i].r = y;
a[i].sum = 0;
a[i].max = 0;
if(x != y){
init(x, mid, left);
init(mid+1, y, right);
}
}
void input(int i, int x, int w)
{
if(x == a[i].l && x == a[i].r){
a[i].sum = w;
a[i].max = w;
}
else{
if(x > mid){
input(right, x, w);
}else{
input(left, x, w);
}
a[i].sum = a[left].sum + a[right].sum;
a[i].max = max(a[left].max, a[right].max);
}
}
int get_sum(int x, int y, int i)
{
if(a[i].l == x && a[i].r == y){
return a[i].sum;
}
if(x > mid){
return get_sum(x, y, right);
}
else if(y <= mid){
return get_sum(x, y, left);
}
else{
return get_sum(x, mid, left) + get_sum(mid+1, y, right);
}
}
int get_max(int x, int y, int i)
{
if(a[i].l == x && a[i].r == y){
return a[i].max;
}
if(x > mid){
return get_max(x, y, right);
}
else if(y <= mid){
return get_max(x, y, left);
}
else{
return max(get_max(x, mid, left), get_max(mid+1, y, right));
}
}
int main()
{
int i, n, m, w, p, x, y;
scanf("%d%d", &n, &m);
init(1, n, 1);
for(i = 1; i <= n; i ++){
scanf("%d", &w);
input(1, i, w);
}
for(i = 0; i < m; i ++){
scanf("%d%d%d", &p, &x, &y);
switch(p){
case 1:
input(1, x, y);
break;
case 2:
printf("%d\n", get_sum(x, y, 1));
break;
case 3:
printf("%d\n", get_max(x, y, 1));
break;
}
}
return 0;
}
举一反三 —— 结点选择
题目背景
有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?
输入输出格式
输入格式
第一行包含一个整数 n 。
接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。
接下来一共 n-1 行,每行描述树上的一条边。
输出格式
输出一个整数,代表选出的点的权值和的最大值。
输入输出样例
输入样例
5 1 2 3 4 5 1 2 1 3 2 4 2 5
输出样例
12
说明 / 提示
选择3、4、5号点,权值和为 3+4+5 = 12 。
参考代码
#include<iostream>
#include<vector>
using namespace std;
vector<int>node[100001];
int dp[100001][2];
int visit[100001];
int value[100001];
void Dfs(int tn)
{
int i;
dp[tn][0]=0;
dp[tn][1]=value[tn];
visit[tn]=1;
for(i=0;i<node[tn].size();i++)
{
int son;
son=node[tn][i];
if(visit[son])
continue;
Dfs(son);
dp[tn][1]=dp[tn][1]+dp[son][0];
dp[tn][0]=dp[tn][0]+max(dp[son][0],dp[son][1]);
}
}
int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}
int main()
{
int n,x,y,Max;
int i;
cin>>n;
for(i=1;i<n+1;i++)
{
cin>>value[i];
visit[i]=0;
}
for(i=1;i<n;i++)
{
cin>>x>>y;
node[x].push_back(y);
node[y].push_back(x);
}
Dfs(1);
Max=dp[1][0];
Max=max(Max,dp[1][1]);
cout<<Max;
return 0;
}