两个小时才会,脑子没带来???
NewMarchqwq · · 题解
看了眼别的题解做法都好妙,感觉妙妙题的妙妙方法学不会了。
先说一个常用技巧。这个题我的做法并没有用上这个,但是因为妙妙做法用了所以记录一下,等会会分别叙述两种做法的。
::::info[trick]
就是两个 Node,分别表示“min(
对于 sum-node:
这两种东西的定义都秉承着“加法分类,乘法分步”的加乘原理,所以代码会很自然。
::::
回到本题。首先看着这个
考虑拆贡献,什么时候
这个等价转化应该很明显,那么现在就变成了下面一个问题:给定
考虑继续转化。容斥。把他变成算“在里面且没有贡献”的个数。也就是说必须包含
处理这个,可以用两种方法。说之前先假装我们已经处理好了其他的问题,变成了“过
继续瞪着
综上,直接在 BIT 上给
::::info[code]
#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = (a), i##ABRACADABRA = (b); i <= i##ABRACADABRA; i++)
#define drep(i, a, b) for (int i = (a), i##ABRACADABRA = (b); i >= i##ABRACADABRA; i--)
using namespace std;
using ll = long long;
constexpr ll mod=1e9+7;
int n,a[200010],ia[200010],pos[200010];
ll ans=0,dp[200010],f[200010],g[200010],w[200010],c[200010];
void add(int o,ll x){for (;o<=n+1;o+=o&-o)(c[o]+=x)%=mod;}
ll ask(int o){ll x=0;for (;o;o&=o-1)x+=c[o];return x%mod;}
void solve(){
scanf("%d",&n),ans=0;
rep(i,1,n)scanf("%d",&a[i]),ia[i]=i;
// 把 a 变成一个排列:相同的数,下标大的要小,方便
sort(ia+1,ia+n+1,[&](int x,int y){
return a[x]==a[y]?x>y:a[x]<a[y];
});
rep(i,1,n)a[ia[i]]=i;
int j=n;
rep(x,1,n){
// 倒着寻找
while (j&&a[j]<x)--j;
pos[ia[x]]=j;
}
rep(i,0,n+5)c[i]=0;
rep(i,1,n)f[i]=1+ask(a[i]-1),add(a[i],f[i]);
rep(i,0,n+5)c[i]=0;
drep(i,n,1)g[i]=1+ask(n-a[i]),add(n-a[i]+1,g[i]);
rep(i,1,n)ans+=f[i]*g[i]%mod;
rep(i,0,n+5)c[i]=0;
// 正片开始
rep(i,1,n){
w[i]=(f[i]+ask(a[i]-1))%mod; // 计算自己答案
add(a[i],w[i]),add(a[pos[i]],mod-w[i]); // 贡献到 a_{p_i}-1
// cout<<i<<' '<<w[i]<<'\n';
}
rep(i,1,n)if (pos[i]==i)ans+=mod-w[i]; // 每个 p,只减一次,因为多个贡献已经上了
printf("%lld\n",ans%mod);
}
int main() {
int tt;
scanf("%d",&tt);
while (tt--)solve();
return 0;
}
::::
题解区看到了另一种做法(credit: @CXY07),非常妙啊!我来按照我的理解讲一下。就是说有:如果
复杂度单只老哥。妙啊。