YbtOJ#791-子集最值【三维偏序】

1|0正题

题目链接:http://www.ybtoj.com.cn/contest/123/problem/1


1|1题目大意

给出3个长度为n的排列A,B,C。然后一个下标集合S的三元组是

(max{Ai},max{Bi},max{Ci})(iS)

求所有下标集合不同的三元组数量
1n105


1|2解题思路

所有下标集合的三元组都能用一个|S|3的集合代替,所以我们只考虑|S|3的就好了。

|S|=1的个数就是n,直接累加即可。

|S|=2的话,那就代表某个下标霸占了两个最大值,而另一个一定是另一个下标的,如果是a,b最大,那么我们就要找满足ai>aj,bi>aj,ci<cj的方案,用三维偏序就好了。

然后a,cb,c的情况也都要做

|S|=3的话很麻烦,考虑容斥,总方案(n3)减去有一个下标是至少两个的最大值。
同样和上面,先考虑a,b,假设下标i满足ai>aj,bi>bj的情况有k种,那么就好有(k2)种情况使得i占据了至少两个最大值。
同理a,cb,c也要做,这是二维偏序,直接树状数组就好了。

但是发现对于i占据了三个最大值的情况我们统计了三次,需要加回多余的两次,那么统计ai>aj,bi>bj,ci>cj的个数k,然后加回k(k1)的方案就好了,这个也要三维偏序

代码里三维偏序用的是CDQ分治+树状数组

时间复杂度O(nlog2n)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #define ll long long #define lowbit(x) (x&-x) using namespace std; const ll N=1e5+10; struct node{ ll a,b,c; }w[N],a[N],b[N]; ll n,ans,sum,t[N],g[N]; void Change(ll x,ll val){ while(x<=n){ t[x]+=val; x+=lowbit(x); } return; } ll Ask(ll x){ ll ans=0; while(x){ ans+=t[x]; x-=lowbit(x); } return ans; } void Merge(ll l,ll mid,ll r){ ll p=l,q=mid+1; for(ll i=1;i<=r-l+1;i++){ if(p<=mid&&w[p].b<=w[q].b||q>r)b[i]=w[p],p++; else b[i]=w[q],q++; } for(ll i=1;i<=r-l+1;i++)w[l+i-1]=b[i]; return; } void CDQ(ll l,ll r,bool op){ if(l==r)return; ll mid=(l+r)>>1; CDQ(l,mid,op);CDQ(mid+1,r,op); ll p=l,tmp; for(ll i=mid+1;i<=r;i++){ while(p<=mid&&w[p].b<w[i].b) Change(w[p].c,1),p++; sum+=(tmp=Ask(w[i].c)); g[w[i].a]+=(op?tmp:0); } for(ll i=l;i<p;i++)Change(w[i].c,-1); Merge(l,mid,r);return; } bool cmp(node x,node y) {return x.a<y.a;} void solve(){ sort(w+1,w+1+n,cmp); for(ll i=1;i<=n;i++){ ll tmp=Ask(w[i].b); ans-=tmp*(tmp-1)/2; Change(w[i].b,1); } memset(t,0,sizeof(t)); return; } signed main() { freopen("subset.in","r",stdin); freopen("subset.out","w",stdout); scanf("%lld",&n);ans=n; for(ll i=1;i<=n;i++)scanf("%lld",&a[i].a); for(ll i=1;i<=n;i++)scanf("%lld",&a[i].b); for(ll i=1;i<=n;i++)scanf("%lld",&a[i].c); for(ll i=1;i<=n;i++) w[i].a=a[i].a,w[i].b=a[i].b,w[i].c=n-a[i].c+1; sort(w+1,w+1+n,cmp);CDQ(1,n,0); for(ll i=1;i<=n;i++) w[i].a=a[i].a,w[i].b=a[i].c,w[i].c=n-a[i].b+1; sort(w+1,w+1+n,cmp);CDQ(1,n,0); for(ll i=1;i<=n;i++) w[i].a=a[i].b,w[i].b=a[i].c,w[i].c=n-a[i].a+1; sort(w+1,w+1+n,cmp);CDQ(1,n,0); ans+=sum;ans+=n*(n-1)*(n-2)/6; for(ll i=1;i<=n;i++)w[i].a=a[i].a,w[i].b=a[i].b;solve(); for(ll i=1;i<=n;i++)w[i].a=a[i].b,w[i].b=a[i].c;solve(); for(ll i=1;i<=n;i++)w[i].a=a[i].a,w[i].b=a[i].c;solve(); for(ll i=1;i<=n;i++) w[i].a=a[i].a,w[i].b=a[i].b,w[i].c=a[i].c; sort(w+1,w+1+n,cmp); CDQ(1,n,1); for(ll i=1;i<=n;i++)ans+=g[i]*(g[i]-1); printf("%lld\n",ans); }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/14441885.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(49)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示