線段樹專題
阿新 • • 發佈:2020-08-04
線段樹專題
https://blog.csdn.net/qq_25605637/article/details/46967529
D-逆序對
D - Minimum Inversion Number
思路
找到第i個數前面有多少比它大的,用vis[x+1]……vis[n-1]求和,從前往後讀,出現過vis就賦為1,求和用線段樹。注意數字是從0到n-1的,線段樹邊界也為這個,但是root還是1。線段樹初始化時候不能從0到n-1賦值為0,因為結點數是n的4倍,所以需要用build初始化。求出原始有多少逆序對後,因為是0..n-1的序列,所以從前往後跑的時候移動每個數能使逆序對減少0..x[i]-1的,增加n-1-x[i]個,每次累計減,取移動完後的逆序對數的最小值。
C++(AC)
#include<iostream> #include<iomanip> #include<cstdlib> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<vector> #include<cmath> #include<map> #define ll long long #define pii pair<int,int> #define mpr make_pair #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f3f3f #define clr(x) memset(x,0,sizeof x) #define rep2(i, x) for(register int i = h[x]; i; i = e[i].nxt) #define rep(i,x,y) if((x)<=(y))for(register int i=(x);i<=(y);i++) #define rrep(i,x,y) if((x)>=(y))for(register int i=(x);i>=(y);i--) #define endl "\n" #define TT() int t;cin>>t;while(t--) #define PI acos(-1) #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int mod=1e9+7; using namespace std; const int maxn=50010; int sum[maxn<<2]; void PushUP(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build(int l,int r,int rt) { sum[rt]=0; if (l==r) { return; } int m=(l+r)>>1; build(lson); build(rson); } void update(int p,int add,int l,int r,int rt) { if (l==r) { sum[rt]+=add; return; } int m=(l+r)>>1; if (p<=m) update(p,add,lson); else update(p,add,rson); PushUP(rt); } int query(int L,int R,int l,int r,int rt) { if (L<=l && r<=R) return sum[rt]; int m=(l+r)>>1; int ret=0; if (L<=m) ret+=query(L,R,lson); if (R>m) ret+=query(L,R,rson); return ret; } int main(){ std::ios::sync_with_stdio(true),cin.tie(0),cout.tie(0); int t,n,l[5010],x[5010],ans=0,Min; while (cin>>n) { ans=0; build(0,n-1,1); rep(i,1,n) { cin>>x[i]; update(x[i],1,0,n-1,1); ans+=query(x[i]+1,n-1,0,n-1,1); } Min=ans; rep(i,1,n) { ans+=n-2*x[i]-1; Min=min(Min,ans); } cout<<Min<<endl; } return 0; }
E-延遲更新
E - Knight Tournament
思路
反向線段覆蓋。全新方法:正向做需要判斷是否和前面衝突的時候,可以直接反著做,將前面的情況覆蓋掉。本題反向進行騎士對戰的勝者覆蓋。然後用線段樹進行區間修改,改了區間累加的程式碼,將所有的相加、比較之類的全部變成直接賦值覆蓋。最後要注意update那裡不能R<L,所以需要判斷是不是勝利的騎士在兩頭。
C++(AC)
#include<iostream> #include<iomanip> #include<cstdlib> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<vector> #include<cmath> #include<map> #define ll long long #define pii pair<int,int> #define mpr make_pair #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f3f3f #define clr(x) memset(x,0,sizeof x) #define rep2(i, x) for(register int i = h[x]; i; i = e[i].nxt) #define rep(i,x,y) if((x)<=(y))for(register int i=(x);i<=(y);i++) #define rrep(i,x,y) if((x)>=(y))for(register int i=(x);i>=(y);i--) #define endl "\n" #define TT() int t;cin>>t;while(t--) #define PI acos(-1) #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int mod=1e9+7; using namespace std; const int maxn=300010; int Min[maxn<<2],add[maxn<<2]; struct data{ int l,r,x; }a[maxn]; void PushDown(int rt) { if (add[rt]) { add[rt<<1] = add[rt]; add[rt<<1|1] = add[rt]; Min[rt<<1] = add[rt]; Min[rt<<1|1] = add[rt]; add[rt] = 0; } } void PushUP(int rt) { Min[rt]=Min[rt<<1]; } void build(int l,int r,int rt) { add[rt] = 0; Min[rt]=0; if (l==r) { return ; } int m=(l+r)>>1; build(lson); build(rson); } int query(int L,int R,int l,int r,int rt) { if (L<=l && r<=R) return Min[rt]; PushDown(rt); int m=(l+r)>>1; int ret=0; if (L<=m) ret=query(L,R,lson); if (R>m) ret=query(L,R,rson); return ret; } void update(int L,int R,int c,int l,int r,int rt) { if (L <= l && r <= R) { add[rt] = c; Min[rt] = c; return ; } PushDown(rt); int m = (l + r) >> 1; if (L <= m) update(L , R , c , lson); if (m < R) update(L , R , c , rson); PushUP(rt); } int main(){ int n,m; std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); cin>>n>>m; build(1,n,1); rep(i,1,m) { cin>>a[i].l>>a[i].r>>a[i].x; } rrep(i,m,1) { if (a[i].l<a[i].x) update(a[i].l,a[i].x-1,a[i].x,1,n,1); if (a[i].r>a[i].x) update(a[i].x+1,a[i].r,a[i].x,1,n,1); } rep(i,1,n) { cout<<query(i,i,1,n,1)<<' '; } return 0; }