Codeforces Round #345 Div.1 D.Zip-line 动态最长上升子序列

题意概述:

给出一个长度为N的序列和M组询问,问假设把某个位置的值改成另一个给出的值之后,序列的最长上升子序列的长度。

N,M<=400000.

分析:

考虑某个位置的值改动后这个位置和最长上升子序列(lis)的关系:

1、这个位置包含在lis中(这种情况答案可能+1,可计算经过这个点的lis来等效决策)。

2、这个位置不包含在lis中,那么需要看是否任意的lis都经过这个位置。如果是的话此决策的结果在原来长度基础上-1,否则就等于原来的长度。

有了大体思路,接下来想想维护。

任务1:对于任意位置x,求包含这个点的lis长度,令g1(i)表示1->N方向长度为i的lis的最长上升子序列的最后一个值的最小值,g2(i)表示N->1方向长度为i的最长下降子序列的最后一个值的最大值(更新就不说了,基础),如果知道位置x状态下恰好未更新的g1,g2数组内容,就同更新一样用前后以这个点结尾的最长序列长度相加-1就是此决策的结果。

任务2:这个实际上是重点,对于任意位置x,知道的东西和任务1一样,同时知道是否存在一对g1(a),g2(b)满足a+b=len(len是原序列lis长度)&&g1(a)<g2(b),如果存在那么此决策的结果为原来答案,否则为原来答案-1。

如果要强行在线的话三棵主席树维护一下两个g数组和一个vis数组(vis(x)表示对于当前状态来说g1(x),g2(len-x)是否合法,当前状态是否合法实际上就是看vis的所有值的和是否不为0)然后随便乱搞就可以了。

然而。。。我选择离线,询问按照位置排序,正反扫一遍序列处理第一种情况,然后再正着扫一遍把第二种情况用一个数组处理了就可以了。能离线何苦去在线码主席树呢(手动滑稽)(手动链表或者vector都可以帮忙离线)?!!

然而我还是弄了半天,污浊的机房CO2,QAQ

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<queue>
 8 #include<set>
 9 #include<map>
10 #include<vector>
11 #include<cctype>
12 using namespace std;
13 const int maxn=400005;
14
15 int N,M,h[maxn];
16 struct que{
17     int id,pos,v;
18     friend bool operator < (que a,que b){
19         return a.pos<b.pos;
20     }
21 }q[maxn];
22 int g1[maxn],g2[maxn],cnt1,cnt2,ans[maxn],len,vis[maxn];
23 struct mlink{
24     static const int max_sz=400005;
25     int np,w[maxn],first[maxn],next[maxn];
26     mlink(){
27         np=0,w[0]=next[0]=0;
28         memset(first,0,sizeof(first));
29     }
30     void ins(int i,int x) { w[++np]=x,next[np]=first[i],first[i]=np; }
31     void del(int i) { first[i]=next[first[i]]; }
32     int val(int i) { return w[first[i]]; }
33 }dd;
34
35 void data_in()
36 {
37     scanf("%d%d",&N,&M);
38     for(int i=1;i<=N;i++) scanf("%d",&h[i]);
39     for(int i=1;i<=M;i++){
40         scanf("%d%d",&q[i].pos,&q[i].v);
41         q[i].id=i;
42     }
43     sort(q+1,q+M+1);
44 }
45 bool cmp(int x,int y) { return x>y; }
46 void work()
47 {
48     for(int i=1;i<=N;i++){
49         int j=lower_bound(q+1,q+M+1,(que){0,i,0})-q;
50         while(j<=M&&q[j].pos==i)
51             ans[q[j].id]=lower_bound(g1+1,g1+cnt1+1,q[j].v)-g1,j++;
52         int x=lower_bound(g1+1,g1+cnt1+1,h[i])-g1;
53         if(x>cnt1) cnt1=x;
54         g1[x]=h[i];
55     }
56     for(int i=N;i>=1;i--){
57         int j=lower_bound(q+1,q+M+1,(que){0,i,0})-q;
58         while(j<=M&&q[j].pos==i)
59             ans[q[j].id]+=lower_bound(g2+1,g2+cnt2+1,q[j].v,cmp)-g2-1,j++;
60         int x=lower_bound(g2+1,g2+cnt2+1,h[i],cmp)-g2;
61         if(x>cnt2) cnt2=x;
62         g2[x]=h[i];
63         dd.ins(x,g2[x]);
64     }
65     memset(g1,0,sizeof(g1)); len=cnt1,cnt1=0;
66     int x=lower_bound(g2+1,g2+cnt2+1,h[1],cmp)-g2,sum=0;
67     dd.del(x); g2[x]=dd.val(x);
68     if(g2[len]) vis[0]=1,sum++;
69     for(int i=1;i<=N;i++){
70         int j=lower_bound(q+1,q+M+1,(que){0,i,0})-q;
71         while(j<=M&&q[j].pos==i)
72             ans[q[j].id]=max(ans[q[j].id],len-(sum==0)),j++;
73         int x1=lower_bound(g1+1,g1+cnt1+1,h[i])-g1;
74         if(x1>cnt1) cnt1=x1; g1[x1]=h[i];
75         int x2=lower_bound(g2+1,g2+cnt2+1,h[i+1],cmp)-g2;
76         dd.del(x2); g2[x2]=dd.val(x2);
77         if(!g2[x2]) cnt2--;
78         if((x1==len||g1[x1]<g2[len-x1])&&!vis[x1]) vis[x1]=1,sum++;//请注意这一句的第一个条件
79         if(g1[len-x2]>=g2[x2]&&vis[len-x2]) vis[len-x2]=0,sum--;
80     }
81     for(int i=1;i<=M;i++) printf("%d\n",ans[i]);
82 }
83 int main()
84 {
85     data_in();
86     work();
87     return 0;
88 }

原文地址:https://www.cnblogs.com/KKKorange/p/8463371.html

时间: 2024-10-10 15:24:22

Codeforces Round #345 Div.1 D.Zip-line 动态最长上升子序列的相关文章

cf之路,1,Codeforces Round #345 (Div. 2)

 cf之路,1,Codeforces Round #345 (Div. 2) ps:昨天第一次参加cf比赛,比赛之前为了熟悉下cf比赛题目的难度.所以做了round#345连试试水的深浅.....       其实这个应该是昨天就写完的,不过没时间了,就留到了今天.. 地址:http://codeforces.com/contest/651/problem/A A. Joysticks time limit per test 1 second memory limit per test 256

Codeforces Round #247 (Div. 2) B - Shower Line

模拟即可 #include <iostream> #include <vector> #include <algorithm> using namespace std; int main(){ vector<int> a(5); for(int i = 0; i < 5; ++ i) a[i] = i; int g[5][5]; for(int i = 0 ; i < 5; ++ i){ for(int j = 0 ; j < 5; ++

Codeforces Round #345 (Div. 2)C. Watchmen(想法题)

传送门 Description Watchmen are in a danger and Doctor Manhattan together with his friend Daniel Dreiberg should warn them as soon as possible. There are n watchmen on a plane, the i-th watchman is located at point (xi, yi). They need to arrange a plan,

A Joysticks (Codeforces Round 345 (Div 2) )

Friends are going to play console. They have two joysticks and only one charger for them. Initially first joystick is charged at a1 percent and second one is charged at a2 percent. You can connect charger to a joystick only at the beginning of each m

Codeforces Round #345 (Div. 2)

D题:直接滑窗,枚举转折点,滑动结束点.水题啊水题.... #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) #define PII pair<int,int> using namespace std; typedef long long ll; const int maxn=1000100; const int INF=1

Codeforces Round #153 (Div. 1)APoints on Line

#include<cstdio> #include<cstring> #include<iostream> using namespace std ; const int maxn  = 100010 ; int a[maxn] ; int bit(int l , int r ,int num) { while(l <= r) { int mid = (l + r) >> 1; if(a[mid] <= num) l = mid + 1 ; el

Codeforces Round #345 (Div. 2) D 细节题

这个题的意思是给你n张首尾相接的照片, 当前照片在1, 每次只能转移一个单位, 转移时间为a, 照片有可能颠倒, 将照片摆正需要的时间为b, 看照片的时间为1, 想要看尽可能多的照片, 问这个数量是多少, 我们可以预处理查看每张照片需要的时间, 然后枚举从左边看需要的时间, 从右边看需要的时间, 二分从另外一边看需要的时间即可, 代码如下: #include <cstdio> #include <algorithm> #include <cstring> using n

Codeforces Round #323 (Div. 2) D. Once Again... 暴力+最长非递减子序列

                                                                              D. Once Again... You are given an array of positive integers a1, a2, ..., an × T of length n × T. We know that for any i > n it is true that ai = ai - n. Find the length of

Codeforces Round #545 (Div. 2)D(KMP,最长公共前后缀,贪心)

#include<bits/stdc++.h>using namespace std;const int N=1000007;char s1[N],s2[N];int len1,len2;int nex[N];int cnt1[7],cnt2[7];int main(){    scanf("%s %s",s1+1,s2+1);    len1=strlen(s1+1);    len2=strlen(s2+1);    for(int i=1;i<=len1;i++