CF283E Cow Tennis Tournament
阿新 • • 發佈:2018-11-03
線段樹+組合數學
這道題直接做好像根本不可做,考慮轉化:我們可以用任意方案數-不合法的方案數,那麼答案為:\[C_n^3 - \sum_{i=1}^{n} C_{ki}^2\]
其中ki為第i個點能打贏的人數。為什麼是這樣的:一個方案不合法只要一個人能打贏兩個人就行了。
怎麼求ki?考慮線段樹,首先將所有人的戰鬥力排序,然後求出每一個操作的左右端點,然後我們按照左端點排序。每次我們掃描到第i個人時就將左端點為i的操作全部實現(用線段樹)然後查詢[i+1,n]中被翻轉了奇數次的,然後查詢[1,i-1]中被翻轉了偶數次的。如果i這個點是某項操作的右端點,那麼操作完後要去掉她的影響(對後面沒有影響了)
收穫:注意補集轉化
code:
#include<iostream> #include<cstdio> #include<queue> #include<algorithm> #include<cstring> #include<cmath> #include<set> #include<map> #include<vector> #define maxn 200000 #define SZJ int #define AK main #define half (l+r)>>1 #define SDOI () using namespace std; #define rep (i,a,b) for (int i=a;i<=b;++i) #define erpe (i,a) for (int i=head[a];i!=-1;i=e[i].next) #define int long long struct hzw { int l,r; }t[maxn]; struct zmd { int lc,rc,tag,sum; }tre[maxn]; int n,k,num[maxn]; vector<int>bkt[100006]; inline bool cmp(hzw a,hzw b) { if (a.l==b.l) return a.r<b.r; else return a.l<b.l; } inline void pushdown(int s,int l,int r) { int mid=half; int tmp=tre[s].tag%2; if (!tmp) {tre[s].tag=0;return;} int lson=tre[s].lc,rson=tre[s].rc; tre[lson].sum=(mid-l+1)-tre[lson].sum,tre[lson].tag+=tre[s].tag; tre[rson].sum=(r-mid)-tre[rson].sum,tre[rson].tag+=tre[s].tag; tre[s].tag=0; } int cnt=0; inline void build(int s,int l,int r) { if (l==r) return; int mid=half; tre[s].lc=++cnt; build(tre[s].lc,l,mid); tre[s].rc=++cnt; build(tre[s].rc,mid+1,r); } inline void update(int s,int l,int r,int cl,int cr) { if (l==cl&&r==cr) { tre[s].sum=(r-l+1)-tre[s].sum; tre[s].tag++; return; } if (tre[s].tag) pushdown(s,l,r); int mid = half; if (cr<=mid) update(tre[s].lc,l,mid,cl,cr); else if (cl>mid) update(tre[s].rc,mid+1,r,cl,cr); else { update(tre[s].lc,l,mid,cl,mid); update(tre[s].rc,mid+1,r,mid+1,cr); } tre[s].sum=tre[tre[s].lc].sum+tre[tre[s].rc].sum; } inline int query(int s,int l,int r,int cl,int cr) { if (l==cl&&r==cr) return tre[s].sum; if (tre[s].tag) pushdown(s,l,r); int mid=half; if (cr<=mid) return query(tre[s].lc,l,mid,cl,cr); else if (cl>mid) return query(tre[s].rc,mid+1,r,cl,cr); else { return query(tre[s].lc,l,mid,cl,mid)+query(tre[s].rc,mid+1,r,mid+1,cr); } } #undef int SZJ AK SDOI { #define int long long cnt=1; cin>>n>>k; for (int i=1;i<=n;++i) cin>>num[i]; sort(num+1,num+1+n); build(1,1,n); num[n+1]=1e17; int fina=n*(n-1)*(n-2)/6; for (int i=1,a,b;i<=k;++i) { cin>>a>>b; t[i].l=lower_bound(num+1,num+1+n,a)-num; t[i].r=upper_bound(num+1,num+1+n+1,b)-num-1; if (t[i].l>t[i].r) t[i].l=1e17,t[i].r=1e17; } sort(t+1,t+1+k,cmp); int now=1; for (int i=1;i<=n;++i) { while (t[now].l==i) update(1,1,n,t[now].l,t[now].r),bkt[t[now].r].push_back(t[now].l),now++; int tmp=0; if (i>1) tmp+=query(1,1,n,1,i-1); if (i<n) tmp+=(n-i)-query(1,1,n,i+1,n); for (int j=0;j<bkt[i].size();++j) update(1,1,n,bkt[i][j],i); fina-=tmp*(tmp-1)/2; } cout<<fina; }