RMQ 算法 RMQ 算法全称为(Range Minimum/Maximum Query),即区间最值查询,解决这种问题有两种比较高效的方法:分别为ST 算法以及线段树。它们两者都要进行一定的预处理。
【ST 算法】
预处理的核心思想是动态规划,通过把每一个点开始的一段区间不断一分为二,直至最后一段的长度为1,然后再一步一步返回回去。代码:
int Log( int N) {
}
void Table() {
for( int i=1; i
int n= Log(N);
for( int j=1; j
for( int i=1; i
if( i +(1
F[i][j]=max( F[i][j-1], F[i+(1
}
若输入:3 2 4 5 6 8 1 2 9 7
若序列生成ST 表如下:
上述代码是求区间最大值的算法,求最小值只需要稍作修改即可。但要注意,为了节省空间,我们按以下规律存储:F[i][j]表示的既是从return log((double)(N))/log(2.0);
第i 个点开始,到第(i+(1
F[i][j]=Max( F[i][j-1], F[i+(1
int RMQ( int nStart, int nEnd) {
int nLog =Log(nEnd-nStart+1);
return max( F[nStart][nLog], F[nEnd-(1
该算法的时间复杂度为O (NlogN )
【线段树】(建造线段树)
和胜者树类似,线段树的空间需求也是4*N。
用递归构建胜者树:
void build(int node, int begin, int end) {
if (begin==end) segTree[node] =array[begin];
else {
build(2*node, begin, (begin+end)/2);
build(2*node+1, (begin+end)/2+1, end);
segTree[node]=max(segTree[2 *node], segTree[2 *node+1]);
}
}
查询与RMQ 原理相似,程序如下:
long long query( int x, int l, int r)
{
if( tree[x].l >=l && tree[x].r
if( tree[x].l > r || tree[x].r
return query( x*2, l, r)+query( x*2+1, l, r);
}
1247逆序对
[题目描述]
给定一个数组A ,它包含N 个整数,分别是A[1],A[2],...A[N]。如果存在下标i 和j ,使得 i
那么A 数组总共有多少对不同的“逆序对”?
范围
第一行为n(1≤n ≤100000) 。
接下来是n 行,每行一个长整型范围内的整数。
分析
此题需要用到离散化、数组计数,需要用线段树进行维护每个区间对应数值出现的数目。不断加入数,然后在线段树中快速查询每一段的和,加起来即可。
下面贴上代码:
#include
#include
#include
#include
#include
using namespace std;
const int ML=100021;
struct node
{
int l,r;
long long sum;
}tree[4*ML];
struct lsh {
int num, p;
}a[ML];
bool _sort(lsh x, lsh y) {
}
int n, m;
long long pm1[ML],pm2[ML];
void buildTree(int x,int l,int r)
{
tree[x].l=l, tree[x].r=r;
if( l==r) { tree[x].sum=0; return ; }
buildTree( x*2 , l , (l+r)/2);
buildTree( x*2+1, 1+((l+r)/2), r );
tree[x].sum=tree[x*2].sum+ tree[x*2+1].sum;
}
void update(int x,int s,int e)
{
update( x*2 , s, e);
update( x*2+1, s, e); if( tree[x].l==s&&tree[x].r==s) { } tree[x].sum+=e; return ; return x.nums || tree[x].r
}
long long query( int x, int l, int r)
{
if( tree[x].l >=l && tree[x].r
if( tree[x].l > r || tree[x].r
return query( x*2, l, r)+query( x*2+1, l, r);
}
long long ans=0;
int main()
{
freopen("1247.in","r",stdin); freopen("1247.out","w",stdout); tree[x].sum= tree[x*2].sum +tree[x*2+1].sum;
scanf("%d", &n);
for( int i=1; i
scanf("%d", &a[i].num); a[i].p=i;
}
buildTree(1,1, n);
sort( a+1, a+n+1, _sort); 离散化
int p=1;
pm1[1] = 1;
for( int i=2; i
}
for ( int i=1; i
} ans+=query(1, pm2[i]+1, 100000); update(1, pm2[i], 1); for( int i=1; i
}
printf("%lld", ans);
return 0;
}
1669碉堡
题目描述
中国长城有N 个碉堡,把长城看成一条数轴,每个碉堡看成数轴上的一个点,第i 个碉堡的坐标是X[i]。每个碉堡都有一个坚固度,第i 个碉堡的坚固度是S[i]。长城是用来抵抗外敌的,所以碉堡之间要联系。任意两个碉堡之间都要联系一次,因此N 个碉堡之间总共会有N ×(N-1)/2次联系。两个碉堡联系一次是需要代价的,比如第i 个碉堡与第j 个碉堡联系一次的代价cost[i][j] = abs(X[i]-X[j]) × max(S[i],S[j]),其中abs 是求绝对值。你的任务是输出N ×(N-1)/2次联系的总代价。
分析
此题跟逆序对十分相似,同样需要用线段树进行数组计数,不断修改线段树,一个一个加入数组,动态维护线段树,然后用logN 查询比a 小的有多少个,比a 大的有多少个。同时用到了乘法分配律来简化时间复杂度。
下面贴上代码:
#include
#include
#include
#include
const int INF=21000000;
using namespace std;
struct zmk {
int l, r; long long sum, s;
}tree[80010];
struct node {
int s,w;
}a[200010];
bool _sort(node x, node y) {
}
void build (int x, int l, int r) {
}
void update(int x,int u,int e) {
} if(tree[x].l==u&&tree[x].r==u) { } if(tree[x].l>u||tree[x].r
long long query1(int x, int l ,int r) {
}
long long query2(int x, int l ,int r) {
}
int main() {
freopen("1669.in", "r", stdin); freopen("1669.out", "w", stdout); int n, _max=-INF; scanf("%d", &n); for( int i=1; i=l&&tree[x].rr||tree[x].r=l&&tree[x].rr||tree[x].r
for( int i=1; i
long long cc=query2(1,a[i].w+1,20000)-a[i].w*query1(1, a[i].w+1, 20000 );
}
在这里强调一下:开数组数据范围要注意,避免溢出等情况。
1670围棋
题目描述
风靡一时的Alpha Go人工智能战胜了人类最顶级的围棋冠军,在业界引起了轰动。然而这与本题关系不大(哈哈),把围棋的棋盘看成是一个平面二维坐标系,把棋子抽象成平面二维坐标系里的一个点,总共有N 个棋子,第i 个棋子的坐标是(X[i],Y[i])。棋子是分“级别的”, 第i 个棋子和第j 个棋子的关系有如下3种:
1、 棋子i 的级别比棋子j 的级别高,当且仅当: X[i] >= X[j]且Y[i] >= Y[j],则棋子i 在棋子j 的右上方区域。
2、 棋子i 的级别比棋子j 的级别低,当且仅当: X[i]
3、 如果都不满足如上两个条件,则棋子i 和棋子j 不具备可比性。 对于第i 个棋子来说,它的级别F[i]就等于级别比它低的棋子的数量。 分析
此题跟逆序对十分相似,同样需要用线段树进行数组计数,不断修改线段树,一个一个加入数组,动态维护线段树,然后用logN 查询比a 小的有多少个,比a 大的有多少个。同时用到了乘法分配律来简化时间复杂度。 } printf("%lld", ans); ans+=(bb+cc)*a[i].s; update(1, a[i].w, 1);
其实这题简直就是水了点,对比逆序对,此题不用离散化,只需要我们将逆序对中的所有下标替换为此题的X 坐标。
下面堵上代码:
#include
#include
#include
#include
const int INF=21000000;
using namespace std;
struct zmk {
struct node {
int s, w, id;
}a[100010];
bool _sort(node x, node y) {
}
int b[100010];
void build (int x, int l, int r) {
}
void update(int x,int u,int e) {
if(tree[x].l==u&&tree[x].r==u) { tree[x].sum+=e; tree[x].l=l, tree[x].r=r; if(l==r) return ; build(x*2, l, (l+r)/2); build(x*2+1, (l+r)/2+1, r); return (x.s
} } if(tree[x].l>u||tree[x].r
long long query(int x, int l ,int r) {
}
int main() {
freopen("1670.in", "r", stdin); freopen("1670.out", "w", stdout); int n, _max=-INF; scanf("%d", &n); for( int i=1; i=l&&tree[x].rr||tree[x].r
}
} update(1, a[i].w, 1); for(int i=1; i
#include
#include
#include
#include
#include
using namespace std;
const int INF=2100083647;
struct node
{
int l, r;
long long max;
}tree[400010];
int n;
long long ans;
long long a[100010];
void buildTree(int x,int l,int r)
{
tree[x].l=l, tree[x].r=r;
if( l==r) { tree[x].max=a[l]; return ; }
buildTree( x*2 , l , (l+r)/2);
buildTree( x*2+1, 1+((l+r)/2), r );
tree[x].max=max(tree[x*2].max,tree[x*2+1].max) ;
}
long long query( int x, int l, int r) {
if( tree[x].l >=l && tree[x].r
if( tree[x].l > r || tree[x].r
}
int erfen(int x) {
}
int main() {
freopen("1667.in","r",stdin); freopen("1667.out","w",stdout); int l=0, r=n+1; while(l+1=a[x]) r=mid; else l=mid;
scanf("%d", &n);
for(int i=1; i
buildTree( 1, 1, n);
for( int i=1; i
ans+=erfen(i)-i;
}
printf("%lld", ans);
return 0; }
1665圆盘
题目描述
桌面上有N 个转盘,从左到右排放,编号从1到N 。每个转盘只能显示0至9中的一个数字。每个转盘有一个小开关,按转盘上的开关,转盘的数字就会自动加1,(如果转盘的数字是9,那么按开关时,转盘的数字就会变成0 )。
Luka 现在玩这个游戏,他有两张白纸。
Luka 重复以下的步骤m 次(注意:下面的3个步骤要次序执行):
(1)选两个整数A 、 B (1 ≤ A ≤ B ≤ N) 并且把它们写到第一张白纸上。
(2)把编号在区间[A,B]内(包括A 、B )的所有转盘显示的数字逐一的加起来,把结果写到第二张白纸上。
(3)对编号在区间[A,B]内(包括A 、B )的转盘,都按一次开关。 做完M 次这样的操作后,请你输出第二张白纸的数据。
分析
这题还是十分水的,动态维护十颗线段树,第一次做没经验…… 下面堵上代码:
#include
#include
#include
#include
#include
using namespace std;
struct node {
int l, r, add; long long sum; int n[10];
}tree[2000010];
int N, M;
char a[2500010];
void build( int x, int l, int r) {
tree[x].l=l, tree[x].r=r;
if( l==r) { tree[x].sum=a[l]-'0'; tree[x].n[a[l]-'0']++; return ; }
build( x*2 , l ,(l+r)/2);
build( x*2+1,1+((l+r)/2), r );
tree[x].sum= tree[x*2].sum+ tree[x*2+1].sum;
for( int i=0; i
}
long long _dat( int x) {
long long Sum=0;
int t[12];
for( int i=0; i
for( int i=0; i
return Sum;
}
void pushdown(int x) {
tree[x].sum=_dat(x);
tree[ x*2 ].add+=tree[x].add;
tree[x*2+1].add+=tree[x].add;
//tree[ x*2 ].sum=_dat(x*2);
//tree[x*2+1].sum=_dat(x*2+1);
tree[x].add=0;
}
void update(int x,int l,int r,int k)
{
if( tree[x].l>=l && tree[x].r
tree[x].add+=k;
}
long long query( int x, int l, int r) {
}
int main() {
freopen("1665.in","r",stdin); freopen("1665.out","w",stdout); scanf("%d %d", &N, &M); scanf("\n"); for( int i=1; i r || tree[x].r =l && tree[x].r r || tree[x].r
}
}*/ } scanf("%d %d", &aa, &bb); for( int j=aa; j= 10) a[j]='0'; return 0; build(1, 1, N); } return 0; int aa, bb; for( int i=1; i
1667 发型
题意:有n 头奶牛,从左往右排,每头奶牛都有一个高度,最左边的奶牛往右看,只能看到比它矮的奶牛。求最左边的奶牛所能看到的奶牛数量。
思路:这题从题目可以看出数据具有单调性:往右看右边一旦出现第一头奶牛比它高,无论后面的奶牛高度是多少,肯定也看不到那些奶牛。根据它的单调性及数据范围,我们可以想到二分的方法。二分一头奶牛的位置,如果从最左边的奶牛到这头奶牛的区间内,如果最高奶牛的高度小于最左边奶牛的高度,就证明还可以看到更多的奶牛,
就把区间缩小到mid-R ,反之则缩小到L-mid 。求区间内的最大值,我们很自然地可以想到线段树,做一遍线段树,判断一下,修改区间就解决这题了。
时间复杂度O(n log n log n)
参考程序:
#include
#include
#include
#include
#include
using namespace std;
const int INF=2100083647;
struct node
{
int l, r;
long long max;
}tree[400010];
int n;
long long ans;
long long a[100010];
void buildTree(int x,int l,int r)
{
tree[x].l=l, tree[x].r=r;
if( l==r) { tree[x].max=a[l]; return ; }
buildTree( x*2 , l , (l+r)/2);
buildTree( x*2+1, 1+((l+r)/2), r );
tree[x].max=max(tree[x*2].max,tree[x*2+1].max) ;
}
long long query( int x, int l, int r) {
if( tree[x].l >=l && tree[x].r r || tree[x].r
}
int erfen(int x) {
}
int main() {
freopen("1667.in","r",stdin); freopen("1667.out","w",stdout); int l=0, r=n+1; while(l+1=a[x]) r=mid; else l=mid;
scanf("%d", &n);
for(int i=1; i
buildTree( 1, 1, n);
for( int i=1; i
ans+=erfen(i)-i;
}
printf("%lld", ans);
return 0;
}
1668. 垃圾桶
本题采用的是动态规划+线段树求最小值的算法,先将每个区间按开始点排序,再使用动态规划。使用f[i]表示清洁前i 个垃圾桶所需
最小费用maps[i]和mapt[i]表示以i 结尾的清洁工清洁的区间。可以推出状态转移方程:f[i]=min (每个在maps[i]和mapt[i]之间结尾的区间+所需费用,f[i]).
下面贴上部分代码:
int main()
{
freopen ("1668.in","r",stdin);
freopen ("1668.out","w",stdout);
long long m,l,r;
cin>>m>>l>>r;
r=r-l+1;
for (int i=1; i
{
cin>>stw[i].s>>stw[i].t>>stw[i].w;
stw[i].s-=l-1;
stw[i].t-=l-1;
}
sort (stw+1,stw+m+1,xx);
l=1;
build (1,0,r);
for (int i=0; i
{
maps[i]=m+1;
mapt[i]=0;
}
for (int i=m; i; i--) maps[stw[i].t]=i;
for (int i=1; i
update (1,0,f[0]=0);
for (int i=1; i
{ //头 //尾
}
} f[i]=INF; for (int j=maps[i]; j
1666 售票系统
题意:某次列车途经C 个城市,城市编号依次为1到C ,列车上共有S 个座位,每一个售票申请包含三个参数, O 为起点,D 为目的地站,N 为车票张数。售票系统对该售票申请受理或不受理的决定,只有在从O 到D 的区段内列车上都有N 个或N 个以上的空座位时该售票申请才被受理。请你写个程序,实现这个自动售票系统。
思路:求一个区间内的值的问题我们会想到线段树,因为它必须保证区段内都有那么多座位,所以我们要考虑最坏情况,即在线段树内记录该区间的最少座位数,每一次都要进行修改和维护
注意:这里有一个坑,就是终点站不能算入区间内,就是相当于把相邻两个车站之间连一条边,把边看成线段树。简单来说就是在输入时,加一句D - -。
参考程序:
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
long long s[60010],oo=2147483647;
int C,S,R,O,D,N;
struct data
{
long long sum,add;
}tree[4*60010];
void build(long long now, long long L, long long R)
{
if (L==R) {tree[now].sum=s[L];}
else
{
build(now*2,L,(L+R)/2);
build(now*2+1,(L+R)/2+1,R);
tree[now].sum=min(tree[now*2].sum,tree[now*2+1].sum);
}
}
void down(long long root, long long L, long long R)
{
int LS=root*2,RS=root*2+1,mid=(L+R)/2;
tree[LS].add+=tree[root].add;
tree[RS].add+=tree[root].add;
tree[LS].sum+=tree[root].add;
tree[RS].sum+=tree[root].add;
tree[root].add=0;
}
void update(long long now, long long L, long long R, int x, int y, int v)
{
if (x>R || y
if (x=R)
{
tree[now].add+=v; tree[now].sum+=v; return;
}
down(now,L,R);
update(now*2,L,(L+R)/2,x,y,v);
update(now*2+1,(L+R)/2+1,R,x,y,v);
tree[now].sum=min(tree[now*2].sum,tree[now*2+1].sum);
}
long long query(long long now, long long L, long long R, int x, int y) {
if (x>R || y
if (x=R) return tree[now].sum;
down(now,L,R);
return
min(query(now*2,L,(L+R)/2,x,y),query(now*2+1,(L+R)/2+1,R,x,y));
}
int main()
{
freopen("1666.in","r",stdin);
freopen("1666.out","w",stdout);
cin>>C>>S>>R;
for (int i=1; i
build(1,1,C);
for (int i=1; i
{
scanf("%d%d%d",&O,&D,&N); D--;
if (query(1,1,C,O,D)
else
{
update(1,1,C,O,D,-N);
cout
}
}
return 0;
}
RMQ 算法 RMQ 算法全称为(Range Minimum/Maximum Query),即区间最值查询,解决这种问题有两种比较高效的方法:分别为ST 算法以及线段树。它们两者都要进行一定的预处理。
【ST 算法】
预处理的核心思想是动态规划,通过把每一个点开始的一段区间不断一分为二,直至最后一段的长度为1,然后再一步一步返回回去。代码:
int Log( int N) {
}
void Table() {
for( int i=1; i
int n= Log(N);
for( int j=1; j
for( int i=1; i
if( i +(1
F[i][j]=max( F[i][j-1], F[i+(1
}
若输入:3 2 4 5 6 8 1 2 9 7
若序列生成ST 表如下:
上述代码是求区间最大值的算法,求最小值只需要稍作修改即可。但要注意,为了节省空间,我们按以下规律存储:F[i][j]表示的既是从return log((double)(N))/log(2.0);
第i 个点开始,到第(i+(1
F[i][j]=Max( F[i][j-1], F[i+(1
int RMQ( int nStart, int nEnd) {
int nLog =Log(nEnd-nStart+1);
return max( F[nStart][nLog], F[nEnd-(1
该算法的时间复杂度为O (NlogN )
【线段树】(建造线段树)
和胜者树类似,线段树的空间需求也是4*N。
用递归构建胜者树:
void build(int node, int begin, int end) {
if (begin==end) segTree[node] =array[begin];
else {
build(2*node, begin, (begin+end)/2);
build(2*node+1, (begin+end)/2+1, end);
segTree[node]=max(segTree[2 *node], segTree[2 *node+1]);
}
}
查询与RMQ 原理相似,程序如下:
long long query( int x, int l, int r)
{
if( tree[x].l >=l && tree[x].r
if( tree[x].l > r || tree[x].r
return query( x*2, l, r)+query( x*2+1, l, r);
}
1247逆序对
[题目描述]
给定一个数组A ,它包含N 个整数,分别是A[1],A[2],...A[N]。如果存在下标i 和j ,使得 i
那么A 数组总共有多少对不同的“逆序对”?
范围
第一行为n(1≤n ≤100000) 。
接下来是n 行,每行一个长整型范围内的整数。
分析
此题需要用到离散化、数组计数,需要用线段树进行维护每个区间对应数值出现的数目。不断加入数,然后在线段树中快速查询每一段的和,加起来即可。
下面贴上代码:
#include
#include
#include
#include
#include
using namespace std;
const int ML=100021;
struct node
{
int l,r;
long long sum;
}tree[4*ML];
struct lsh {
int num, p;
}a[ML];
bool _sort(lsh x, lsh y) {
}
int n, m;
long long pm1[ML],pm2[ML];
void buildTree(int x,int l,int r)
{
tree[x].l=l, tree[x].r=r;
if( l==r) { tree[x].sum=0; return ; }
buildTree( x*2 , l , (l+r)/2);
buildTree( x*2+1, 1+((l+r)/2), r );
tree[x].sum=tree[x*2].sum+ tree[x*2+1].sum;
}
void update(int x,int s,int e)
{
update( x*2 , s, e);
update( x*2+1, s, e); if( tree[x].l==s&&tree[x].r==s) { } tree[x].sum+=e; return ; return x.nums || tree[x].r
}
long long query( int x, int l, int r)
{
if( tree[x].l >=l && tree[x].r
if( tree[x].l > r || tree[x].r
return query( x*2, l, r)+query( x*2+1, l, r);
}
long long ans=0;
int main()
{
freopen("1247.in","r",stdin); freopen("1247.out","w",stdout); tree[x].sum= tree[x*2].sum +tree[x*2+1].sum;
scanf("%d", &n);
for( int i=1; i
scanf("%d", &a[i].num); a[i].p=i;
}
buildTree(1,1, n);
sort( a+1, a+n+1, _sort); 离散化
int p=1;
pm1[1] = 1;
for( int i=2; i
}
for ( int i=1; i
} ans+=query(1, pm2[i]+1, 100000); update(1, pm2[i], 1); for( int i=1; i
}
printf("%lld", ans);
return 0;
}
1669碉堡
题目描述
中国长城有N 个碉堡,把长城看成一条数轴,每个碉堡看成数轴上的一个点,第i 个碉堡的坐标是X[i]。每个碉堡都有一个坚固度,第i 个碉堡的坚固度是S[i]。长城是用来抵抗外敌的,所以碉堡之间要联系。任意两个碉堡之间都要联系一次,因此N 个碉堡之间总共会有N ×(N-1)/2次联系。两个碉堡联系一次是需要代价的,比如第i 个碉堡与第j 个碉堡联系一次的代价cost[i][j] = abs(X[i]-X[j]) × max(S[i],S[j]),其中abs 是求绝对值。你的任务是输出N ×(N-1)/2次联系的总代价。
分析
此题跟逆序对十分相似,同样需要用线段树进行数组计数,不断修改线段树,一个一个加入数组,动态维护线段树,然后用logN 查询比a 小的有多少个,比a 大的有多少个。同时用到了乘法分配律来简化时间复杂度。
下面贴上代码:
#include
#include
#include
#include
const int INF=21000000;
using namespace std;
struct zmk {
int l, r; long long sum, s;
}tree[80010];
struct node {
int s,w;
}a[200010];
bool _sort(node x, node y) {
}
void build (int x, int l, int r) {
}
void update(int x,int u,int e) {
} if(tree[x].l==u&&tree[x].r==u) { } if(tree[x].l>u||tree[x].r
long long query1(int x, int l ,int r) {
}
long long query2(int x, int l ,int r) {
}
int main() {
freopen("1669.in", "r", stdin); freopen("1669.out", "w", stdout); int n, _max=-INF; scanf("%d", &n); for( int i=1; i=l&&tree[x].rr||tree[x].r=l&&tree[x].rr||tree[x].r
for( int i=1; i
long long cc=query2(1,a[i].w+1,20000)-a[i].w*query1(1, a[i].w+1, 20000 );
}
在这里强调一下:开数组数据范围要注意,避免溢出等情况。
1670围棋
题目描述
风靡一时的Alpha Go人工智能战胜了人类最顶级的围棋冠军,在业界引起了轰动。然而这与本题关系不大(哈哈),把围棋的棋盘看成是一个平面二维坐标系,把棋子抽象成平面二维坐标系里的一个点,总共有N 个棋子,第i 个棋子的坐标是(X[i],Y[i])。棋子是分“级别的”, 第i 个棋子和第j 个棋子的关系有如下3种:
1、 棋子i 的级别比棋子j 的级别高,当且仅当: X[i] >= X[j]且Y[i] >= Y[j],则棋子i 在棋子j 的右上方区域。
2、 棋子i 的级别比棋子j 的级别低,当且仅当: X[i]
3、 如果都不满足如上两个条件,则棋子i 和棋子j 不具备可比性。 对于第i 个棋子来说,它的级别F[i]就等于级别比它低的棋子的数量。 分析
此题跟逆序对十分相似,同样需要用线段树进行数组计数,不断修改线段树,一个一个加入数组,动态维护线段树,然后用logN 查询比a 小的有多少个,比a 大的有多少个。同时用到了乘法分配律来简化时间复杂度。 } printf("%lld", ans); ans+=(bb+cc)*a[i].s; update(1, a[i].w, 1);
其实这题简直就是水了点,对比逆序对,此题不用离散化,只需要我们将逆序对中的所有下标替换为此题的X 坐标。
下面堵上代码:
#include
#include
#include
#include
const int INF=21000000;
using namespace std;
struct zmk {
struct node {
int s, w, id;
}a[100010];
bool _sort(node x, node y) {
}
int b[100010];
void build (int x, int l, int r) {
}
void update(int x,int u,int e) {
if(tree[x].l==u&&tree[x].r==u) { tree[x].sum+=e; tree[x].l=l, tree[x].r=r; if(l==r) return ; build(x*2, l, (l+r)/2); build(x*2+1, (l+r)/2+1, r); return (x.s
} } if(tree[x].l>u||tree[x].r
long long query(int x, int l ,int r) {
}
int main() {
freopen("1670.in", "r", stdin); freopen("1670.out", "w", stdout); int n, _max=-INF; scanf("%d", &n); for( int i=1; i=l&&tree[x].rr||tree[x].r
}
} update(1, a[i].w, 1); for(int i=1; i
#include
#include
#include
#include
#include
using namespace std;
const int INF=2100083647;
struct node
{
int l, r;
long long max;
}tree[400010];
int n;
long long ans;
long long a[100010];
void buildTree(int x,int l,int r)
{
tree[x].l=l, tree[x].r=r;
if( l==r) { tree[x].max=a[l]; return ; }
buildTree( x*2 , l , (l+r)/2);
buildTree( x*2+1, 1+((l+r)/2), r );
tree[x].max=max(tree[x*2].max,tree[x*2+1].max) ;
}
long long query( int x, int l, int r) {
if( tree[x].l >=l && tree[x].r
if( tree[x].l > r || tree[x].r
}
int erfen(int x) {
}
int main() {
freopen("1667.in","r",stdin); freopen("1667.out","w",stdout); int l=0, r=n+1; while(l+1=a[x]) r=mid; else l=mid;
scanf("%d", &n);
for(int i=1; i
buildTree( 1, 1, n);
for( int i=1; i
ans+=erfen(i)-i;
}
printf("%lld", ans);
return 0; }
1665圆盘
题目描述
桌面上有N 个转盘,从左到右排放,编号从1到N 。每个转盘只能显示0至9中的一个数字。每个转盘有一个小开关,按转盘上的开关,转盘的数字就会自动加1,(如果转盘的数字是9,那么按开关时,转盘的数字就会变成0 )。
Luka 现在玩这个游戏,他有两张白纸。
Luka 重复以下的步骤m 次(注意:下面的3个步骤要次序执行):
(1)选两个整数A 、 B (1 ≤ A ≤ B ≤ N) 并且把它们写到第一张白纸上。
(2)把编号在区间[A,B]内(包括A 、B )的所有转盘显示的数字逐一的加起来,把结果写到第二张白纸上。
(3)对编号在区间[A,B]内(包括A 、B )的转盘,都按一次开关。 做完M 次这样的操作后,请你输出第二张白纸的数据。
分析
这题还是十分水的,动态维护十颗线段树,第一次做没经验…… 下面堵上代码:
#include
#include
#include
#include
#include
using namespace std;
struct node {
int l, r, add; long long sum; int n[10];
}tree[2000010];
int N, M;
char a[2500010];
void build( int x, int l, int r) {
tree[x].l=l, tree[x].r=r;
if( l==r) { tree[x].sum=a[l]-'0'; tree[x].n[a[l]-'0']++; return ; }
build( x*2 , l ,(l+r)/2);
build( x*2+1,1+((l+r)/2), r );
tree[x].sum= tree[x*2].sum+ tree[x*2+1].sum;
for( int i=0; i
}
long long _dat( int x) {
long long Sum=0;
int t[12];
for( int i=0; i
for( int i=0; i
return Sum;
}
void pushdown(int x) {
tree[x].sum=_dat(x);
tree[ x*2 ].add+=tree[x].add;
tree[x*2+1].add+=tree[x].add;
//tree[ x*2 ].sum=_dat(x*2);
//tree[x*2+1].sum=_dat(x*2+1);
tree[x].add=0;
}
void update(int x,int l,int r,int k)
{
if( tree[x].l>=l && tree[x].r
tree[x].add+=k;
}
long long query( int x, int l, int r) {
}
int main() {
freopen("1665.in","r",stdin); freopen("1665.out","w",stdout); scanf("%d %d", &N, &M); scanf("\n"); for( int i=1; i r || tree[x].r =l && tree[x].r r || tree[x].r
}
}*/ } scanf("%d %d", &aa, &bb); for( int j=aa; j= 10) a[j]='0'; return 0; build(1, 1, N); } return 0; int aa, bb; for( int i=1; i
1667 发型
题意:有n 头奶牛,从左往右排,每头奶牛都有一个高度,最左边的奶牛往右看,只能看到比它矮的奶牛。求最左边的奶牛所能看到的奶牛数量。
思路:这题从题目可以看出数据具有单调性:往右看右边一旦出现第一头奶牛比它高,无论后面的奶牛高度是多少,肯定也看不到那些奶牛。根据它的单调性及数据范围,我们可以想到二分的方法。二分一头奶牛的位置,如果从最左边的奶牛到这头奶牛的区间内,如果最高奶牛的高度小于最左边奶牛的高度,就证明还可以看到更多的奶牛,
就把区间缩小到mid-R ,反之则缩小到L-mid 。求区间内的最大值,我们很自然地可以想到线段树,做一遍线段树,判断一下,修改区间就解决这题了。
时间复杂度O(n log n log n)
参考程序:
#include
#include
#include
#include
#include
using namespace std;
const int INF=2100083647;
struct node
{
int l, r;
long long max;
}tree[400010];
int n;
long long ans;
long long a[100010];
void buildTree(int x,int l,int r)
{
tree[x].l=l, tree[x].r=r;
if( l==r) { tree[x].max=a[l]; return ; }
buildTree( x*2 , l , (l+r)/2);
buildTree( x*2+1, 1+((l+r)/2), r );
tree[x].max=max(tree[x*2].max,tree[x*2+1].max) ;
}
long long query( int x, int l, int r) {
if( tree[x].l >=l && tree[x].r r || tree[x].r
}
int erfen(int x) {
}
int main() {
freopen("1667.in","r",stdin); freopen("1667.out","w",stdout); int l=0, r=n+1; while(l+1=a[x]) r=mid; else l=mid;
scanf("%d", &n);
for(int i=1; i
buildTree( 1, 1, n);
for( int i=1; i
ans+=erfen(i)-i;
}
printf("%lld", ans);
return 0;
}
1668. 垃圾桶
本题采用的是动态规划+线段树求最小值的算法,先将每个区间按开始点排序,再使用动态规划。使用f[i]表示清洁前i 个垃圾桶所需
最小费用maps[i]和mapt[i]表示以i 结尾的清洁工清洁的区间。可以推出状态转移方程:f[i]=min (每个在maps[i]和mapt[i]之间结尾的区间+所需费用,f[i]).
下面贴上部分代码:
int main()
{
freopen ("1668.in","r",stdin);
freopen ("1668.out","w",stdout);
long long m,l,r;
cin>>m>>l>>r;
r=r-l+1;
for (int i=1; i
{
cin>>stw[i].s>>stw[i].t>>stw[i].w;
stw[i].s-=l-1;
stw[i].t-=l-1;
}
sort (stw+1,stw+m+1,xx);
l=1;
build (1,0,r);
for (int i=0; i
{
maps[i]=m+1;
mapt[i]=0;
}
for (int i=m; i; i--) maps[stw[i].t]=i;
for (int i=1; i
update (1,0,f[0]=0);
for (int i=1; i
{ //头 //尾
}
} f[i]=INF; for (int j=maps[i]; j
1666 售票系统
题意:某次列车途经C 个城市,城市编号依次为1到C ,列车上共有S 个座位,每一个售票申请包含三个参数, O 为起点,D 为目的地站,N 为车票张数。售票系统对该售票申请受理或不受理的决定,只有在从O 到D 的区段内列车上都有N 个或N 个以上的空座位时该售票申请才被受理。请你写个程序,实现这个自动售票系统。
思路:求一个区间内的值的问题我们会想到线段树,因为它必须保证区段内都有那么多座位,所以我们要考虑最坏情况,即在线段树内记录该区间的最少座位数,每一次都要进行修改和维护
注意:这里有一个坑,就是终点站不能算入区间内,就是相当于把相邻两个车站之间连一条边,把边看成线段树。简单来说就是在输入时,加一句D - -。
参考程序:
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
long long s[60010],oo=2147483647;
int C,S,R,O,D,N;
struct data
{
long long sum,add;
}tree[4*60010];
void build(long long now, long long L, long long R)
{
if (L==R) {tree[now].sum=s[L];}
else
{
build(now*2,L,(L+R)/2);
build(now*2+1,(L+R)/2+1,R);
tree[now].sum=min(tree[now*2].sum,tree[now*2+1].sum);
}
}
void down(long long root, long long L, long long R)
{
int LS=root*2,RS=root*2+1,mid=(L+R)/2;
tree[LS].add+=tree[root].add;
tree[RS].add+=tree[root].add;
tree[LS].sum+=tree[root].add;
tree[RS].sum+=tree[root].add;
tree[root].add=0;
}
void update(long long now, long long L, long long R, int x, int y, int v)
{
if (x>R || y
if (x=R)
{
tree[now].add+=v; tree[now].sum+=v; return;
}
down(now,L,R);
update(now*2,L,(L+R)/2,x,y,v);
update(now*2+1,(L+R)/2+1,R,x,y,v);
tree[now].sum=min(tree[now*2].sum,tree[now*2+1].sum);
}
long long query(long long now, long long L, long long R, int x, int y) {
if (x>R || y
if (x=R) return tree[now].sum;
down(now,L,R);
return
min(query(now*2,L,(L+R)/2,x,y),query(now*2+1,(L+R)/2+1,R,x,y));
}
int main()
{
freopen("1666.in","r",stdin);
freopen("1666.out","w",stdout);
cin>>C>>S>>R;
for (int i=1; i
build(1,1,C);
for (int i=1; i
{
scanf("%d%d%d",&O,&D,&N); D--;
if (query(1,1,C,O,D)
else
{
update(1,1,C,O,D,-N);
cout
}
}
return 0;
}