中石油训练赛 - Check List(线段树维护偏序问题)
生活随笔
收集整理的這篇文章主要介紹了
中石油训练赛 - Check List(线段树维护偏序问题)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目大意:給出 n 個點,需要計算出滿足下列條件的三元對 ( i , j , k ) 的數量:
題目分析:可以先對 x 進行排序,然后從左到右枚舉每一個點去計算其作為三元對中的第一個點、第二個點和第三個點時的貢獻
先想一個簡單版本的問題,如果將偏序問題轉換為:
這樣該如何去求解呢?這個模型相對就比較簡單了,可以直接用線段樹在 y 軸上維護一些變量:
更新的話也比較簡單,因為所有點已經對 x 軸進行排序了,所以只需要按照如下順序更新即可,假設當前枚舉到的點為 ( x , y ):
時間復雜度是 nlogn 的,下面思考一下類比于上面的方法來解決本題
仍然是用線段樹在?y 軸上維護一些變量:
類比于上面的更新,設當前點為 ( x , y ):
需要注意的點是:
代碼:
#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;//有多少個第一個點LL sum2;//有多少個第一個點與第二個點的匹配關系LL lazy;//sum2的下傳標記 }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)//更新第一個點的貢獻 {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)//更新第一個點與第二個點的匹配貢獻 {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)//返回第一個點和第二個點的匹配貢獻 {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)//更新答案(作為第三個點的貢獻){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)//更新第一個點和第二個點的匹配關系 {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)//更新第一個點的貢獻 {int y=get_id(p[pos].y);update1(1,y);pos++;}i=pos-1;}printf("%lld\n",ans);return 0; }?
總結
以上是生活随笔為你收集整理的中石油训练赛 - Check List(线段树维护偏序问题)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中石油训练赛 - Trading Car
- 下一篇: 中石油训练赛 - Russian Dol