您的位置:首页 > 其它

JZOJ 3742. 【TJOI2014】上升子序列

2017-04-05 19:21 323 查看

Description



Input



Output



Sample Input

4

1 2 3 3

Sample Output

4

Data Constraint

对于30%的数据,N<=5000

对于100%的数据,N<=10^5

Solution

这题看上去有些熟悉,但却无从下手……

在赛后,才猛然醒悟到——权值线段树!

线段树上维护一个值 a[i] ,以 a[i] 为结尾的上升子序列的方案数。

为了处理方便,可以将长度为 1 的也记进去。

那么 a[i] 显然可以由 1~a[i] 的值推出,再加上自己一个独立的,即:fai=1+∑j=1i−1faj

就等于在权值线段树上查询值为 [1,a[i]−1] 和即可,在用单点修改把结果赋到 a[i] 上。

那么这些每个位置的值都可以一次 O(NlogN) 推出(扫描+查询)。

接着来统计答案,扫一遍,查询当前 a[i] 的值,减一后累加进答案(把之前多算的减去)

然后将 a[i] 这个位置上的值用单点修改清零,避免重复计算。

所以总时间复杂度为 O(NlogN) 。

Code

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=100001,mo=1e9+7;
struct data
{
int x,y;
}a
;
int tot=1;
LL ans,sum;
LL f[N*4];
int b
;
inline int read()
{
int data=0,w=1; char ch=0;
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') ch=getchar(),w=-1;
while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
return data*w;
}
inline bool cmp(data a,data b)
{
return a.x<b.x;
}
inline void find(LL v,LL l,LL r,LL x,LL y)
{
if(l==x && r==y)
{
ans=(ans+f[v])%mo;
return;
}
int mid=(l+r)>>1;
if(y<=mid) find(v*2,l,mid,x,y); else
if(x>mid) find(v*2+1,mid+1,r,x,y); else
{
find(v*2,l,mid,x,mid);
find(v*2+1,mid+1,r,mid+1,y);
}
}
inline void change(LL v,LL l,LL r,LL x,LL y)
{
if(l==r)
{
f[v]=y;
return;
}
int mid=(l+r)>>1;
if(x<=mid) change(v*2,l,mid,x,y); else change(v*2+1,mid+1,r,x,y);
f[v]=(f[v*2]+f[v*2+1])%mo;
}
int main()
{
int n=read();
for(int i=1;i<=n;i++) a[a[i].y=i].x=read();
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(a[i].x!=a[i-1].x) tot++;
b[a[i].y]=tot;
}
for(int i=1;i<=n;i++)
{
ans=0;
if(b[i]>1) find(1,1,tot,1,b[i]-1);
change(1,1,tot,b[i],ans+1);
}
for(int i=1;i<=n;i++)
{
ans=0;
find(1,1,tot,b[i],b[i]);
if(ans)
{
sum=(sum+ans-1)%mo;
change(1,1,tot,b[i],0);
}
}
printf("%lld",sum);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: