中石油训练赛 - Check List(线段树维护偏序问题)
生活随笔
收集整理的這篇文章主要介紹了
中石油训练赛 - Check List(线段树维护偏序问题)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
題目大意:給出 n 個(gè)點(diǎn),需要計(jì)算出滿足下列條件的三元對 ( i , j , k ) 的數(shù)量:
題目分析:可以先對 x 進(jìn)行排序,然后從左到右枚舉每一個(gè)點(diǎn)去計(jì)算其作為三元對中的第一個(gè)點(diǎn)、第二個(gè)點(diǎn)和第三個(gè)點(diǎn)時(shí)的貢獻(xiàn)
先想一個(gè)簡單版本的問題,如果將偏序問題轉(zhuǎn)換為:
這樣該如何去求解呢?這個(gè)模型相對就比較簡單了,可以直接用線段樹在 y 軸上維護(hù)一些變量:
更新的話也比較簡單,因?yàn)樗悬c(diǎn)已經(jīng)對 x 軸進(jìn)行排序了,所以只需要按照如下順序更新即可,假設(shè)當(dāng)前枚舉到的點(diǎn)為 ( x , y ):
時(shí)間復(fù)雜度是 nlogn 的,下面思考一下類比于上面的方法來解決本題
仍然是用線段樹在?y 軸上維護(hù)一些變量:
類比于上面的更新,設(shè)當(dāng)前點(diǎn)為 ( x , y ):
需要注意的點(diǎn)是:
代碼:
#pragma GCC optimize(2) #pragma GCC optimize("Ofast","inline","-ffast-math") #pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;vector<int>node;struct Point {int x,y;bool operator<(const Point& t)const{return x<t.x;} }p[N];struct Node {int l,r;LL sum1;//有多少個(gè)第一個(gè)點(diǎn)LL sum2;//有多少個(gè)第一個(gè)點(diǎn)與第二個(gè)點(diǎn)的匹配關(guān)系LL lazy;//sum2的下傳標(biāo)記 }tree[N<<2];void pushup(int k) {tree[k].sum1=tree[k<<1].sum1+tree[k<<1|1].sum1;tree[k].sum2=tree[k<<1].sum2+tree[k<<1|1].sum2; }void pushdown(int k) {if(tree[k].lazy){LL lz=tree[k].lazy;tree[k].lazy=0;tree[k<<1].sum2+=tree[k<<1].sum1*lz;tree[k<<1|1].sum2+=tree[k<<1|1].sum1*lz;tree[k<<1].lazy+=lz;tree[k<<1|1].lazy+=lz;} }void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;tree[k].sum1=tree[k].sum2=tree[k].lazy=0;if(l==r)return;int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r); }void update1(int k,int pos)//更新第一個(gè)點(diǎn)的貢獻(xiàn) {if(tree[k].l==tree[k].r){tree[k].sum1++;return;}pushdown(k);int mid=tree[k].l+tree[k].r>>1;if(pos<=mid)update1(k<<1,pos);elseupdate1(k<<1|1,pos);pushup(k); }void update2(int k,int l,int r)//更新第一個(gè)點(diǎn)與第二個(gè)點(diǎn)的匹配貢獻(xiàn) {if(tree[k].r<l||tree[k].l>r)return;if(tree[k].l>=l&&tree[k].r<=r){tree[k].sum2+=tree[k].sum1;tree[k].lazy++;return;}pushdown(k);update2(k<<1,l,r);update2(k<<1|1,l,r);pushup(k); }LL query(int k,int l,int r)//返回第一個(gè)點(diǎn)和第二個(gè)點(diǎn)的匹配貢獻(xiàn) {if(tree[k].r<l||tree[k].l>r)return 0;if(tree[k].l>=l&&tree[k].r<=r)return tree[k].sum2;pushdown(k);return query(k<<1,l,r)+query(k<<1|1,l,r); }void discreate() {sort(node.begin(),node.end());node.erase(unique(node.begin(),node.end()),node.end()); }int get_id(int x) {return lower_bound(node.begin(),node.end(),x)-node.begin()+1; }int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d%d",&p[i].x,&p[i].y);node.push_back(p[i].y);}sort(p+1,p+1+n);discreate();build(1,1,node.size());LL ans=0;for(int i=1,pos;i<=n;i++){pos=i;while(pos<=n&&p[pos].x==p[i].x)//更新答案(作為第三個(gè)點(diǎn)的貢獻(xiàn)){int y=get_id(p[pos].y);ans+=query(1,1,y-1);pos++;}pos=i;while(pos<=n&&p[pos].x==p[i].x)//更新第一個(gè)點(diǎn)和第二個(gè)點(diǎn)的匹配關(guān)系 {int y=get_id(p[pos].y);update2(1,y+1,node.size());pos++;}pos=i;while(pos<=n&&p[pos].x==p[i].x)//更新第一個(gè)點(diǎn)的貢獻(xiàn) {int y=get_id(p[pos].y);update1(1,y);pos++;}i=pos-1;}printf("%lld\n",ans);return 0; }?
總結(jié)
以上是生活随笔為你收集整理的中石油训练赛 - Check List(线段树维护偏序问题)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中石油训练赛 - Trading Car
- 下一篇: 中石油训练赛 - Russian Dol