P4528 [CTSC2008] 图腾 题解
来一个比较优雅的做法。
约定在
首先考虑这种怎么数。
这个可以用这种情况:
减掉这种情况得到。
然后观察剩下的两个,它们分别长这样:
这种每出现一个答案
这种每出现一个答案
观察发现这两种的区别在于第
这启发我们把四排列计数换成三排列计数。
也就是,把四个点的贡献拆到三个点上。
对于这种情况:
只要它的剩下一个点在第一个点右上方并且不与这两个红点重复,就直接把答案
而对于另一种情况:
只要它的剩下一个点在第一个点右上方并且不与这两个红点重复,就直接把答案
此时应该使答案
然后就可以考虑对于第一个点右上方三个点的每一种排列,对答案的贡献是多少。
这种情况贡献是
这种情况贡献是
这种情况贡献是
这种情况贡献是
这种情况贡献是
这种情况贡献是
发现除了我们要算入答案的情况
非常妙的一题,出题人巧妙地用了
然后这道题到这里就做完了,只需要跑三遍扫描线,代码也是不难的。
这里贴上代码
#include<bits/stdc++.h>
#define pii pair<long long,long long>
#define mp make_pair
#define pb push_back
using namespace std;
unsigned int n,a[200005],b[200005],d[200005],ans=0,sum=0,ans1=0,ans2=0;
struct szsz {
unsigned int v[200005];
inline void change(int wz,unsigned x) {
for(; wz<=n; wz+=wz&-wz)v[wz]+=x;
}
inline unsigned int check(int wz,unsigned int res=0) {
for(; wz; wz-=wz&-wz)res+=v[wz];
return res;
}
};
szsz c,c1,c2;
int main() {
// freopen("d.in","r",stdin);
// freopen("d.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1; i<=n; i++) {
cin>>a[i];
d[i]=c2.check(a[i]);
c2.change(a[i],1);
}
for(int i=n; i>=1; i--) {
b[i]=(n-i)-c.check(a[i]);
unsigned int t=sum-c1.check(a[i]);
ans+=(__int128)b[i]*(b[i]-1)*(b[i]-2)/6;
if(b[i]>2) {
ans+=2*t*(b[i]-2);
ans-=b[i]*(b[i]-1)/2*(b[i]-2);
}
ans1+=t*d[i];
ans2+=b[i]*(b[i]-1)/2*d[i];
sum+=b[i];
c1.change(a[i],b[i]);
c.change(a[i],1);
}
// cout<<ans1<<" "<<ans2<<"???\n";
ans2-=ans1;
ans-=4*ans1;
ans-=2*ans2;
ans>>=1;
ans-=ans2;
cout<<ans%(1<<24);
return 0;
}