hdu 5773 The All-purpose Zero(Multi J,给你n个数(n<=1e5),每个数小于等于1000000,其中数为0的那些数可以变成其他任意的数,问变化之后的最长上升子序)
来源:互联网 发布:解放战争胜利原因知乎 编辑:程序博客网 时间:2024/06/05 03:08
传送门:hdu 5773 The All-purpose Zero
题意:给你n个数(n<=1e5),每个数小于等于1000000,其中数为0的那些数可以变成其他任意的数,问变化之后的最长上升子序列。
思路一:
首先猜想一个结论:那些为0的数一定全部在最长上升子序列中
反证法:假设至少一个0不在最长上升子序列中,那么将这个0的后面一个位置的数替换为0,最长上升子序列的长度一定不变。
那么我们可以想到一个做法,每遇到一个0,就将其后面的数全部减一,表示这个0必取,那么剩下的数的最长上升子序列+0的个数就是答案。
#include<bits/stdc++.h>using namespace std;const int maxn=101000;int a[maxn],b[maxn];int main(){ int _,n; scanf("%d",&_); for(int case1=1;case1<=_;case1++){ scanf("%d",&n); int pre=0,cnt=0; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); if(a[i]==0) pre++; else a[i]-=pre,a[++cnt]=a[i]; } int len=0,pos; if(cnt>=1){ b[1]=a[1]; len=1; for(int i=2;i<=cnt;i++){ if(b[len]<a[i]) len++,b[len]=a[i]; else pos=lower_bound(b+1,b+len+1,a[i])-b,b[pos]=a[i]; } } printf("Case #%d: %d\n",case1,len+pre); } return 0;}
思路二:
dp[i]是表示上升子序列长度为i的最小值
如果有一个0要插入,那就dp[i]+1->dp[i+1],然后dp[1]=负无穷
->序列右移->线段树上预留出很多位置,起点左移
#include<bits/stdc++.h>using namespace std;const int maxn=200100;const int inf=0x3f3f3f3f;int lazy[maxn<<2],maxv[maxn<<2],a[maxn];#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1int L,R,Midpoint,tot,v; //Midpoint表示刚开始的起点的位置void pushup(int rt){ maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);}void build(int l,int r,int rt){ lazy[rt]=0; if(l==r){ if(l<Midpoint) maxv[rt]=-inf; else maxv[rt]=inf; return ; } int m=(l+r)>>1; build(lson); build(rson); pushup(rt);}void pushdown(int rt){ maxv[rt<<1]+=lazy[rt],maxv[rt<<1|1]+=lazy[rt]; lazy[rt<<1]+=lazy[rt],lazy[rt<<1|1]+=lazy[rt]; lazy[rt]=0;}void update(int l,int r,int rt){ if(l==r){ maxv[rt]=v; return ; } pushdown(rt); int m=(l+r)>>1; if(L<=m&&maxv[rt<<1]>=v) update(lson); else update(rson); pushup(rt);}int query(int l,int r,int rt){ if(L<=l&&R>=r) return maxv[rt]; int m=(l+r)>>1; if(L<=m) return query(lson); else return query(rson);}int main(){ int _,n; scanf("%d",&_); for(int case1=1;case1<=_;case1++){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); Midpoint=n+5,tot=n*2+10; build(1,tot,1); for(int i=1;i<=n;i++){ if(a[i]==0){ ++lazy[1]; L=--Midpoint,R=L,v=-inf; update(1,tot,1); } else v=a[i],L=n+5,R=tot,update(1,tot,1); } int ans=0; for(int i=Midpoint;i<=tot;i++){ L=i,R=i; if(query(1,tot,1)>=inf) break; ans=i-Midpoint+1; } printf("Case #%d: %d\n",case1,ans); } return 0;}
思路三:
cdq+扫描线+树状数组
假设我们现在要求len[x]=max( len[i]+min(a[x]-a[i]-1,pre[x]-pre[i]) )+1
假设a[x]>a[i],我们对min(a[x]-a[i]-1,pre[x]-pre[i])进行讨论,维护一个三维偏序
1.a[x]-a[i]-1<=pre[x]-pre[i]-> a[x]-pre[x]-1<=a[i]-pre[i],这时候加上的最大值便为len[i]+(a[x]-a[i]-1)
2.a[x]-a[i]-1>pre[x]-pre[i]-> a[x]-pre[x]-1>a[i]-pre[i],这时候加上的最大值便为len[i]+(pre[x]-pre[i])
#include<bits/stdc++.h>using namespace std;const int inf=0x3f3f3f3f;const int maxn=200100;int m,pre[maxn],a[maxn],B[maxn],b[maxn],ans,T,len[maxn];int qa[maxn],qb[maxn],pos0[maxn],pos1[maxn],maxv0[maxn],maxv1[maxn],tot,num,C[maxn];bool cmp(int x,int y){ return a[x]<a[y];}void add0(int x,int y){ for(int i=x;i>0;i-=(i&-i)){ if(pos0[i]!=T) maxv0[i]=y,pos0[i]=T; else maxv0[i]=max(maxv0[i],y); }}void add1(int x,int y){ for(int i=x;i<=num;i+=(i&-i)){ if(pos1[i]!=T) maxv1[i]=y,pos1[i]=T; else maxv1[i]=max(maxv1[i],y); }}void ask0(int &ans,int y,int x){ while(x<=num){ if(pos0[x]==T) ans=max(ans,y+maxv0[x]); x+=(x&-x); }}void ask1(int &ans,int y,int x){ while(x>0){ if(pos1[x]==T) ans=max(ans,y+maxv1[x]); x-=(x&-x); }}void solve(int l,int r){ if(l==r){ len[l]++; return ; } int mid=l+r>>1; solve(l,mid); int Count_a=0,Count_b=0; for(int i=l;i<=mid;i++) qa[Count_a++]=i; for(int i=mid+1;i<=r;i++) qb[Count_b++]=i; sort(qa,qa+Count_a,cmp),sort(qb,qb+Count_b,cmp); int id=0,i; for(T++,i=0;i<Count_b;i++){ while(id<Count_a&&a[qa[id]]<a[qb[i]]){ add0(b[qa[id]],len[qa[id]]-a[qa[id]]-1);//比b[qa[id]]小的位置加上len[qa[id]]-a[qa[id]]-1 add1(b[qa[id]],len[qa[id]]-pre[qa[id]]);//相反 id++; } int num1=lower_bound(B+1,B+num+1,a[qb[i]]-pre[qb[i]]-1)-B; ask0(len[qb[i]],a[qb[i]],num1); ask1(len[qb[i]],pre[qb[i]],num1-1); } solve(mid+1,r);}int main(){ int _,n; scanf("%d",&_); for(int case1=1;case1<=_;case1++){ scanf("%d",&n); a[m=1]=-inf,T=0; int Count=0,x; memset(len,0,sizeof(len)); memset(pre,0,sizeof(pre)); memset(pos0,0,sizeof(pos0)); memset(pos1,0,sizeof(pos1)); for(int i=1;i<=n;i++){ scanf("%d",&x); if(x==0) Count++; else pre[++m]=Count,a[m]=x; } pre[++m]=Count,a[m]=inf,tot=0; for(int i=1;i<=m;i++) B[++tot]=b[i]=a[i]-pre[i],B[++tot]=b[i]-1; sort(B+1,B+tot+1); num=unique(B+1,B+tot+1)-B-1; for(int i=1;i<=m;i++) b[i]=lower_bound(B+1,B+num+1,b[i])-B; solve(1,m); printf("Case #%d: %d\n",case1,len[m]-2); } return 0;}
0 0
- hdu 5773 The All-purpose Zero(Multi J,给你n个数(n<=1e5),每个数小于等于1000000,其中数为0的那些数可以变成其他任意的数,问变化之后的最长上升子序)
- 2N+1个数,除了其中一个数之外,其他均为两两成对,寻找这个落单的数
- 欧拉函数,求小于等于n 的数里与 n 互质的数的个数
- 已知一个整数n,写一个函数f(n),返回0~n间,每个数中出现的“1”的个数,问最大的F(n)= n中n为多少
- 从n个数中找出每个数的重复数
- 随机生成一个小于等于N的数
- (java)统计小于N的数中素数的个数
- 取值为[1,n-1] 含n 个元素的整数数组至少存在一个重复数,O(n) 时间内找出其中任意一个重复数
- 求0~n里面每个数二进制1的个数,用o(n)算法
- pku3678 2_SAT现有N个数,每个数不是0就是1。现给出一些有关这些数的与,或,异或的关系,问可能不?
- 【转】求小于等于N的与N互质的数的和
- 【转AekdyCoin】求小于等于N的与N互质的数的和
- 【转AekdyCoin】求小于等于N的与N互质的数的和
- 【AekdyCoin】求小于等于N的与N互质的数的和
- 将一个整数M分成N个数相加的和,要求每个数至少要大于等于1
- 【欧拉函数】(小于或等于n的数中与n互质的数的数目)
- HDU5430欧拉函数求小于等于n的数中与n互质的数的数目
- N SUM 数组中任意数相加的结果等于剩下的数相加和
- 前端的小玩意(7)——自动给每个字下方加着重号
- 区别:Thread.currentThread().getContextClassLoader() and Class.getClassLoader()
- 支付宝移动支付开发错误集锦
- AcEdJig类
- DexHunter学习笔记记录
- hdu 5773 The All-purpose Zero(Multi J,给你n个数(n<=1e5),每个数小于等于1000000,其中数为0的那些数可以变成其他任意的数,问变化之后的最长上升子序)
- 欢迎使用CSDN-markdown编辑器
- Android Studio使用NDK编译自己的.so库
- 欢迎使用CSDN-markdown编辑器
- iOS开发- 常见日志打印
- 操作系统——进程管理
- LINUX FTP用户的创建
- 解析目前NFC具有的三种工作模式
- 考的不好,不想多说