NOIP2015

金币
国王将金币作为工资,发放给忠诚的骑士。第一天,骑士收到一枚金币;之后两天(第二天和第三天),每天收到两枚金币;之后三天(第四、五、六天),每天收到三枚金币;之后四天(第七、八、九、十天),每天收到四枚金币,这种工资发放模式会一直这样延续下去:当连续n天收到n枚金币时,骑士会在之后的连续n+1天中,每天收到n+1枚金币
请计算在前k天中,其实一共获得了多少枚金币。
水题,枚举即可


扫雷游戏是一款十分经典的单机小游戏。在 n 行
m 列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格)。玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有多少个是地雷格。游戏的目标是在不翻出任何地雷格的条件下,找出所有的非地雷格。
现在给出
n 行 m 列的雷区中的地雷分布,要求计算出每个非地雷格周围的地雷格数。
注:一个格子的周围格子包括其上、下、左、右、左上、右上、左下、右下八个方向上与之直接相邻的格子。
水题,没什么好说的,暴力即可


P2671 [NOIP 2015 普及组] 求和
一条狭长的纸带被均匀划分出了 n 个格子,格子编号从 1 到 n。每个格子上都染了一种颜色color\(_{i}\)(用[1,m]当中的一个整数表示]),并且写了一个数字number\(_{i}\)
这里定义一种特殊的三元组:(x,y,z),其中x,y,z都表示纸带上格子的编号。这里的三元组要求满足以下条件:
1.x,y,z都是整肃,x<y<z,y-x=z-y
2.color\(_{x}\) = color\(_{z}\)
满足上述条件的三元组的分数规定为 (x+z)×(numberx​+numberz​)。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以 10007 所得的余数即可
1≤n≤100000,1≤m≤100000,1≤colori\(_{i}\)≤m,1≤number\(_{i}\)​≤100000
首先考虑x,y,z的位置如何选择
1)注意到y-x=z-y,也就是要求我们选择的x与z的奇偶性相同
2)注意到color\(_{x}\) = color\(_{z}\),直接地告诉我们x,z的颜色要相同
整理得在选择x,z时要求x,z的奇偶性相同,颜色相同
考虑如何计算价值,我们将式子拆分为:x number\(_{x}\) + z number\(_{x}\) + x* number\(_{z}\) + z* number\(_{z}\)**
可以分别维护 z ,number\(_{z}\) , z number\(_{z}\)*
我们可以枚举x,则z必定在x之后,所以从后往前枚举,实现一边计算一边维护
注意到每当枚举到一个x时,x的价值为在x之后所有满足条件的z与x进行配对的总和。
我们考虑维护出在x之后所有满足条件的z能提供的信息:所有z的总和,所有number\(_{z}\)的总和,所有z * number\(_{z}\)的总和,同时注意到我们每次配对时x*number\(_{x}\)也会被计算多次,所以再维护满足条件的z数量cnt,当前x能提供贡献当且仅当cnt>0
于是我们设sum\(_{1/2/3/4,i,0/1}\)
分别为维护4个信息,颜色为,奇偶性,从后往前进行后缀和,时间复杂度O(n)


阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有 N 家住户,第 i 家住户到入口的距离为 Si​ 米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的 X 家住户推销产品,然后再原路走出去。
阿明每走 1 米就会积累 1 点疲劳值,向第 i 家住户推销产品会积累 Ai​ 点疲劳值。阿明是工作狂,他想知道,对于不同的 X,在不走多余的路的前提下,他最多可以积累多少点疲劳值
考虑dp,易得转移方程f\(_{i}\) = f\(_{i-1}\) + 当前可选择的最大贡献点
维护当前的最大贡献,我们考虑使用线段树、
在选择了一个最大值(下标为i)后,整体的每个贡献也会发生改变,这里我们分情况讨论
1)i本身:因为i已经被选,所以对应i这个贡献直接清0
2)在i之前:因为如果要选择i,则必须走到i对应的S\(_{i}\)
也就是在S\(_{i}\)前的每家住户都会被经过,这时如果要选择在S\(_{i}\)前的住户则无需考虑路程的贡献,即在S\(_{i}\)前的每一家住户在线段树上对应的点需要分别减去它们对应的路程贡献(S\(_{j}\) * 2),但要注意的是,如果之前就被减去了路程贡献,则以后都不再减了,所以维护一个标记数组vis,标记当前区间内的每一个住户的路程贡献是否被被减过
3)在i之后,同2),选择i就必须走到S\(_{i}\)
也就是说如果之后还要再选择S\(_{i}\)之后的住户,可以看作以S\(_{i}\)为起点
即在S\(_{i}\)之后的点需要减去i的路程贡献(2 * S\(_{i}\))
但要注意的是:如果之前选择过在S\(_{i}\)之后的点,那么就不需要再减去S\(_{i}\) * 2了,因为我们视作以S较大的点为新起点,所以我们再次维护一个标记数组vis2,维护当前区间减去路程的最大值,若现在要减去的路程不大于之前减去过的最大值,则不需要减,否则减去应减的值2*(S\(_{i}\) - vis2)
代码如下

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+7,INF=1e9;
struct node{
	int id,ma;
}t[N<<2];
int tag[N<<2],tag2[N<<2],val[N],n;
int f[N],s[N];
bool vis3[N<<2];
int vis[N<<2];
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((pl+pr)/2)
void up(int p){
	if(t[ls].ma>=t[rs].ma)
		t[p].ma=t[ls].ma,t[p].id=t[ls].id;
	else 
		t[p].ma=t[rs].ma,t[p].id=t[rs].id;
}
void build(int p,int pl,int pr){
	if(pl==pr){
		t[p].ma=val[pl]+s[pl]*2;
		t[p].id=pl;
		return;
	}
	build(ls,pl,mid);
	build(rs,mid+1,pr);
	up(p);
}
void down(int p){
	if(!tag[p]) return;
	if(tag[p]){
		t[ls].ma-=s[t[ls].id]*2,tag[ls]=tag[p];
		t[rs].ma-=s[t[rs].id]*2,tag[rs]=tag[p];
		tag[p]=0;
	}else if(tag2[p]){
		t[ls].ma+=tag2[p],tag2[ls]+=tag2[p];
		t[rs].ma+=tag2[p],tag2[rs]+=tag2[p];
		tag2[p]=0;
	}
}
void upd(int L,int R,int p,int pl,int pr,int id){
	if(pl>pr) return;
	int w;
	if(vis[p]>id) return;
	else w=-2*(s[id]-s[vis[p]]);
	if(pl>=L && pr<=R){
		tag2[p]+=w;
		t[p].ma+=w;
		vis[p]=id;
		return;
	}
	down(p);
	if(L<=mid) upd(L,R,ls,pl,mid,id);
	if(R>mid) upd(L,R,rs,mid+1,pr,id);
	up(p);
}
void upd3(int L,int R,int p,int pl,int pr){
	if(pl>pr) return;
	if(vis3[p]) return;
	if(pl>=L && pr<=R) vis3[p]=1;
	if(pl==pr){
		t[p].ma-=s[pl]*2;
		s[pl]=0;
		vis3[p]=1;
		return;
	}
	down(p);
	if(L<=mid) upd3(L,R,ls,pl,mid);
	if(R>mid) upd3(L,R,rs,mid+1,pr);
	up(p);
}
void upd2(int idx,int p,int pl,int pr){
	if(pl>pr) return;
	if(pl==pr){
		t[p].ma=0;
		return;
	}
	down(p);
	if(idx<=mid) upd2(idx,ls,pl,mid);
	else upd2(idx,rs,mid+1,pr);
	up(p);
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>s[i];
	for(int i=1;i<=n;i++) cin>>val[i];
	build(1,1,n);
	for(int i=1;i<=n;i++){
		f[i]=f[i-1]+t[1].ma;
        int rank1=t[1].id;
		upd2(rank1,1,1,n);
		upd(rank1+1,n,1,1,n,rank1);
		upd3(1,rank1-1,1,1,n);
        //cout<<t[1].ma<<" "<<t[1].id<<"\n";
		cout<<f[i]<<"\n";
	}
}
posted on 2025-10-20 09:28  Jklans  阅读(11)  评论(0)    收藏  举报