lightoj1085 线段树+dp

  1 //Accepted    7552 KB    844 ms
  2 //dp[i]=sum(dp[j])+1 j<i && a[j]<a[i]
  3 //可以用线段树求所用小于a[i]的dp[j]的和
  4 //需要离散化
  5 #include <cstdio>
  6 #include <cstring>
  7 #include <iostream>
  8 #include <queue>
  9 #include <cmath>
 10 #include <algorithm>
 11 using namespace std;
 12 /**
 13   * This is a documentation comment block
 14   * 如果有一天你坚持不下去了,就想想你为什么走到这儿!
 15   * @authr songt
 16   */
 17 const int imax_n = 100005;
 18 const int pp= 1000000007;
 19 struct node
 20 {
 21     int l,r;
 22     long long sum;
 23 }f[imax_n*3];
 24 void build(int t,int l,int r)
 25 {
 26     f[t].l=l;
 27     f[t].r=r;
 28     f[t].sum=0;
 29     if (l==r)
 30     {
 31         return ;
 32     }
 33     int mid=(l+r)/2;
 34     build(2*t,l,mid);
 35     build(2*t+1,mid+1,r);
 36 }
 37 long long query(int t,int l,int r)
 38 {
 39     if (f[t].l==l && f[t].r==r)
 40     {
 41         return f[t].sum%pp;
 42     }
 43     int mid=(f[t].l+f[t].r)/2;
 44     if (r<=mid) return query(2*t,l,r)%pp;
 45     else
 46     {
 47         if (l>mid) return query(2*t+1,l,r)%pp;
 48         else return (query(2*t,l,mid)+query(2*t+1,mid+1,r))%pp;
 49     }
 50 }
 51 void update(int t,int l,int r,int c)
 52 {
 53     if (f[t].l==l && f[t].r==r)
 54     {
 55         f[t].sum=(f[t].sum+(r-l+1)*c%pp)%pp;
 56         return ;
 57     }
 58     f[t].sum=(f[t].sum+(r-l+1)*c%pp)%pp;
 59     int mid=(f[t].l+f[t].r)/2;
 60     if (r<=mid) update(2*t,l,r,c);
 61     else
 62     {
 63         if (l>mid) update(2*t+1,l,r,c);
 64         else
 65         {
 66             update(2*t,l,mid,c);
 67             update(2*t+1,mid+1,r,c);
 68         }
 69     }
 70     //f[t].sum=(f[2*t].sum+f[2*t+1].sum)%pp;
 71 }
 72 int n;
 73 struct anode
 74 {
 75     int a,b;
 76     int tid;
 77 }a[imax_n];
 78 int cmp(anode x,anode y)
 79 {
 80     return x.a<y.a;
 81 }
 82 int cmp2(anode x,anode y)
 83 {
 84     return x.tid<y.tid;
 85 }
 86 void pre()
 87 {
 88     sort(a+1,a+n+1,cmp);
 89     int k=2;
 90     a[1].b=2;
 91     int i=2;
 92     while (i<=n)
 93     {
 94         if (a[i].a==a[i-1].a)
 95         {
 96             a[i].b=k;
 97         }
 98         else
 99         {
100             a[i].b=k+1;
101             k++;
102         }
103         i++;
104     }
105     sort(a+1,a+n+1,cmp2);
106    // for (int i=1;i<=n;i++)
107    // {
108    //     printf("%d ",a[i].b);
109    // }
110     //printf("\n");
111 }
112 void slove()
113 {
114     long long ans=0;
115     build(1,1,n+2);
116     pre();
117     for (int i=1;i<=n;i++)
118     {
119         int t=query(1,1,a[i].b-1)%pp;
120         t++;
121         ans=(ans+t)%pp;
122         update(1,a[i].b,a[i].b,t);
123     }
124     printf("%lld\n",ans);
125 }
126 int main()
127 {
128     //printf("%d\n",2*pp);
129     int T;
130     scanf("%d",&T);
131     for (int t=1;t<=T;t++)
132     {
133         scanf("%d",&n);
134         for (int i=1;i<=n;i++)
135         {
136             scanf("%d",&a[i].a);
137             a[i].tid=i;
138         }
139         printf("Case %d: ",t);
140         slove();
141     }
142     return 0;
143 }

时间: 2024-10-12 19:10:05

lightoj1085 线段树+dp的相关文章

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

ZOJ3632 线段树+DP

买西瓜吃,每个西瓜有两个参数,一个是p代表价格,一个是t代表能吃几天,要求n天每天都能吃西瓜,而且如果你今天买了,以前买的还没吃完 那么都得扔了,求最小花费,还真想不到用线段树+DP,最后看了一下别人的标题,想了一下,DP方程挺好推的,线段树也只是单点查询, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #inclu

HDU6447 YJJ&#39;s Salesman 2018中国大学生程序设计竞赛 - 网络选拔赛1010 离散化+线段树+DP

YJJ's Salesman Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 253    Accepted Submission(s): 62 Problem Description YJJ is a salesman who has traveled through western country. YJJ is always on

[HDU 6447][YJJ&#39;s Salesman][2018CCPC网络选拔赛 1010][离散化+线段树+DP]

链接: http://acm.hdu.edu.cn/showproblem.php?pid=6447 题意: 左上角(0,0),右下角(10^9,10^9)的网格,其中有n(1<=n<=10^5)个方格内有权值. 一次只能沿右,下,右下三个方向走一个格子,只有沿右下方向走到格子里才可以获得权值. 问从(0,0)到(10^9,10^9)的路径最大权值是多少. 思路: 网格路径权值问题,第一感考虑DP,x从上往下,y从左往右刷表,状态转移方程为dp[i][j]=max(dp[i-1][j],dp[

CF932F(李超线段树+dp)

CF932F(李超线段树+dp) 此题又是新玩法, 李超线段树合并优化\(dp\) 一个显然的\(\Theta(n^2)dp\): \(dp[x]\)表示从x出发到叶子节点的最小代价 \(dp[x] = \min(dp[y] + a[x] * b[y]) ~~(y \in subtree(x))\) 如果我们将\(b[y]\)看成斜率, \(dp[y]\)看成纵截距, \(a[x]\)看成横坐标, 那么问题转为了在平面上有一些直线, 选出与直线\(x = a[x]\)相交的最靠下的点吗, 李超线

【BZOJ1835】[ZJOI2010]base 基站选址 线段树+DP

[BZOJ1835][ZJOI2010]base 基站选址 Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位置,使得总费用最小. 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述. 第

hdu4521(线段树+dp)

传送门:小明系列问题——小明序列 题意:有n个数,求间距大于d的最长上升序列. 分析:dp[i]表示在i点以a[i]结束距离大于d的最长上升序列,然后每更新到第i点时,取i-d之前小于a[i]的数为结束的最长上升序列进行状态转移,并维护前i-d之前的最大上升序列,维护i-d之前的每点为结束的最长上升序列用线段树维护即可. #pragma comment(linker,"/STACK:1024000000,1024000000") #include <cstdio> #inc

codeforces#426(div1) B - The Bakery (线段树 + dp)

题意:把 n 个数划分成 m 段,要求每组数不相等的数的数量最大之和. 思路: dp方程 : dp[i][j] = max( dp[k][j-1] + v(k, i) );( j<=k<i , k = j, j+1, +...+ i-1) dp[i][j]表示第 i 个数分到第 j 段的最大值. v(k, i) 表示k~i中不同数的个数,此处用hash记录每个数上一次出现的位置,从上一次出现的位置到当前位置的 dp[i][j-1] 值均可+1. 此时时间复杂度 O(n*m*log(n)). 线

hdu 4747 线段树/DP

先是线段树 可以知道mex(i,i),mex(i,i+1)到mex(i,n)是递增的. 首先很容易求得mex(1,1),mex(1,2)......mex(1,n) 因为上述n个数是递增的. 然后使用线段树维护,需要不断删除前面的数. 比如删掉第一个数a[1]. 那么在下一个a[1]出现前的 大于a[1]的mex值都要变成a[1] 因为是单调递增的,所以找到第一个 mex > a[1]的位置,到下一个a[1]出现位置,这个区间的值变成a[1]. 然后需要线段树实现区间修改和区间求和 #inclu