HDU5367 思维map // 动态线段树
地主毛毛有n座山,这些山在地主家门前排成一条直线。这些山一开始均有相同的高度。 每一天,毛毛都会要求花花开挖机把几座山挖掉一定高度,或者给一些山堆上一些高度。并且要求花花报告现在有多少座山属于“高山脉”当一排山的高度相等,并且比这排山左边和右边的山要高时,这排山被称为高山脉。当然,最左边和最右边的山不可能是“高山脉”的一部分
这题乍一看可以用线段树做,事实上确实可以用线段树做,但是在仔细思考,讨论出所有情况之后发现可以修改端点代替修改区间的方法来AC
也就是说,将每次修改的L和R变为修改单点,用一次solve来将L之后的所有山全部加上v,再用一次solve来将R + 1之后的山高度全部减去R,在修改的过程中利用map来修改ans可以实现题目要求的在线
1.map的AC代码
#include <map> #include <set> #include <cmath> #include <queue> #include <stack> #include <vector> #include <string> #include <cstdio> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #include <functional> #define For(i, x, y) for(int i=x; i<=y; i++) #define _For(i, x, y) for(int i=x; i>=y; i--) #define Mem(f, x) memset(f, x, sizeof(f)) #define Sca(x) scanf("%d", &x) #define Scl(x) scanf("%lld",&x); #define Pri(x) printf("%d\n", x) #define Prl(x) printf("%lld\n",x); #define CLR(u) for(int i = 0; i <= N ; i ++) u[i].clear(); #define LL long long #define ULL unsigned long long #define mp make_pair #define PII pair<int,int> #define PIL pair<int,long long> #define PLL pair<long long,long long> #define pb push_back #define fi first #define se second using namespace std; typedef vector<int> VI; const double eps = 1e-9; const int maxn = 110; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; inline int read() { int now=0;register char c=getchar(); for(;!isdigit(c);c=getchar()); for(;isdigit(c);now=now*10+c-'0',c=getchar()); return now; } int N,Q,R; int ans = 0; map<int,int>P; void solve(int mid,int v){ if(!v)return; map<int,int> ::iterator it,l,r; //cout << mid << " " << P[mid] <<endl; if(P[mid] == 0){ l = r = it = P.find(mid); //这一步要放在if里面 l--,r++; if(l->se > 0 && r->se > 0 && v < 0){ ans += (mid - l->fi); // cout << l->fi << " " << it->fi << " " << r->fi << endl; }else if(l->se > 0 && r->se < 0 && v < 0){ ans -= (r->fi - mid); }else if(l->se < 0 && r->se < 0 && v > 0){ ans += (r->fi - mid); }else if(l->se > 0 && r->se < 0 && v > 0){ ans -= mid - l->fi; } P[mid] = v; }else{ l = r = it = P.find(mid); l--,r++; if(r == P.end() || it == P.begin()) return; //对最左端和最右端的更新 if(l->se > 0 && r->se > 0 && it->se < 0){ ans -= mid - l->fi; }else if(l->se < 0 && r->se < 0 && it->se > 0){ ans -= r->fi - mid; }else if(l->se > 0 && r->se < 0 && it->se < 0){ ans += r->fi - mid; }else if(l->se > 0 && r->se < 0 && it->se > 0){ ans += mid - l->fi; } // cout << it->fi << " " << it->se << endl; v += it->se; P.erase(it); solve(mid,v); } } int main() { while(~scanf("%d%d%d",&N,&Q,&R)){ P.clear(); P[1] = -INF; P[N + 1] = INF; ans = 0; while(Q--){ int l,r,v; scanf("%d%d%d",&l,&r,&v); l ^= ans; r ^= ans; v ^= ans; solve(l,v); solve(r + 1,-v); Pri(ans); } } return 0; }
2.有了以上的算法,从实现难度上来讲线段树就不算是最优解了,但是对练习线段树的写法也有一定意义,所以我用线段树也实现了一次。
由于N大到1e9次,就不能像普通线段树一样直接build建树,要通过动态加点,也就是说只开辟使用到的区间的空间,由于查询只有50000次,仅仅开辟使用的空间并不会导致mle,学习了一手动态线段树的写法,这就很舒服
这次线段树的难度主要在于pushup的实现上已经怎么想到维护的点,去维护什么东西,还有动态线段树的实现
以下是动态线段树的代码
#include <map> #include <set> #include <cmath> #include <queue> #include <stack> #include <vector> #include <string> #include <cstdio> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #include <functional> #define For(i, x, y) for(int i=x; i<=y; i++) #define _For(i, x, y) for(int i=x; i>=y; i--) #define Mem(f, x) memset(f, x, sizeof(f)) #define Sca(x) scanf("%d", &x) #define Scl(x) scanf("%lld",&x); #define Pri(x) printf("%d\n", x) #define Prl(x) printf("%lld\n",x); #define CLR(u) for(int i = 0; i <= N ; i ++) u[i].clear(); #define LL long long #define ULL unsigned long long #define mp make_pair #define PII pair<int,int> #define PIL pair<int,long long> #define PLL pair<long long,long long> #define pb push_back #define fi first #define se second using namespace std; typedef vector<int> VI; const double eps = 1e-9; const int maxn = 100010; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; inline int read() { int now=0;register char c=getchar(); for(;!isdigit(c);c=getchar()); for(;isdigit(c);now=now*10+c-'0',c=getchar()); return now; } int ans; int N,Q,R; struct Tree{ int lt,rt; int sum; //高山脉的数目 int lsum,rsum; //左右起高度相同的山脉 int lh,rh; //左右山脉的高度 int ll,rr; //左右第一个不连续山脉的高度 int lazy; void init(int l,int r){ sum = lh = rh = ll = rr = lazy = lt = rt = 0; lsum = rsum = r - l + 1; } }tree[maxn * 100]; int tot; void check(int &t,int l,int r){ if(t) return; t = ++tot; tree[t].init(l,r); if(l == 1) tree[t].ll = INF; if(r == N) tree[t].rr = INF; } void add(int t,int val){ tree[t].lazy += val; tree[t].ll += val; tree[t].rr += val; tree[t].lh += val; tree[t].rh += val; } void Pushdown(int t,int l,int r){ if(tree[t].lazy){ int m = (l + r) >> 1; check(tree[t].lt,l,m); check(tree[t].rt,m + 1,r); add(tree[t].rt,tree[t].lazy); add(tree[t].lt,tree[t].lazy); tree[t].lazy = 0; } } void Pushup(int t,int l,int r){ int m = (l + r) >> 1; int lt = tree[t].lt; int rt = tree[t].rt; check(lt,l,m); check(rt,m + 1,r); tree[t].sum = tree[lt].sum + tree[rt].sum; tree[t].lsum = tree[lt].lsum; tree[t].rsum = tree[rt].rsum; tree[t].lh = tree[lt].lh; tree[t].rh = tree[rt].rh; tree[t].ll = tree[lt].ll; tree[t].rr = tree[rt].rr; if(tree[lt].rh == tree[rt].lh){ if(tree[lt].rh > tree[lt].rr && tree[rt].ll < tree[rt].lh){ tree[t].sum += tree[lt].rsum + tree[rt].lsum; } if(tree[lt].rsum == m - l + 1){ tree[t].lsum += tree[rt].lsum; tree[t].ll = tree[rt].ll; } if(tree[rt].lsum == r - m){ tree[t].rsum += tree[lt].rsum; tree[t].rr = tree[lt].rr; } }else{ int lson = lt; int rson = rt; if(tree[lson].lsum == m - l + 1){ tree[t].ll = tree[rson].lh; } if(tree[lson].rh > tree[rson].lh && tree[lson].rh > tree[lson].rr){ tree[t].sum += tree[lson].rsum; } if(tree[rson].rsum == r - m){ tree[t].rr = tree[lson].rh; } if(tree[rson].lh > tree[lson].rh && tree[rson].lh > tree[rson].ll){ tree[t].sum += tree[rson].lsum; } } } void update(int &t,int l,int r,int L,int R,int val){ check(t,l,r); if(L <= l && r <= R){ add(t,val); return; } Pushdown(t,l,r); int m = (l + r) >> 1; if(L <= m) update(tree[t].lt,l,m,L,R,val); if(R > m) update(tree[t].rt,m + 1,r,L,R,val); Pushup(t,l,r); } int main() { while(~scanf("%d%d%d",&N,&Q,&R)){ ans = 0; tot = 0; int root = 0; while(Q--){ int l,r,val; scanf("%d%d%d",&l,&r,&val); l ^= ans; r ^= ans; val ^= ans; if(l > r) swap(l,r); update(root,1,N,l,r,val); ans = tree[root].sum; Pri(ans); } } return 0; }