bzoj5158 [TJOI2014]Alice and Bob

xiaoxiao2021-02-28  36

http://www.elijahqi.win/archives/3112 题目描述

Alice和Bob发明了一个新的游戏。给定一个序列{x0,x1,…,xn-1}。Alice得到一个序列{a0,a1,…,an-1},其中a;表示以x;结尾的最长上升子序列的长度;Bob得到一个序列{b0,b1,…,bn-1},其中bi表示以xi开头的最长下降子序列的长度。 Alice的得分是序列{a0,a1,…,an-1}的和,Bob的得分是{b0,b1,…,bn-1}的和。

输入输出格式

输入格式:

输入的第一行是n,第二行是序列{a0,a1,……’,an-1}。数据保证序列a可以由至少一个1到n的排列得到

输出格式:

输出包含一行,表示Bob能得到的最高分数

输入输出样例

输入样例#1: 复制

4 1 2 2 3 输出样例#1: 复制

5 输入样例#2: 复制

4 1 1 2 3 输出样例#2: 复制

5 说明

数据范围 对于 30% 的数据,N ≤ 1000

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

真是sb 树状数组求dp都yy了很久 直接到这来做思维难度直线下降

考虑贪心的去构造方案 模拟nlog(n) 求最长上升子序列的方法 这时候如果a相同 那么我必然让他们的c的权值递降更好 这样显然会对我b的答案产生更多帮助所以就每次记录一下最后一次出现的位置 然后直接从0开始dfs一下即可 因为加边..的顺序是反的.. 然后就构造出了c 然后求最长下降子序列 用树状数组倒序求即可

#include<cstdio> #include<cctype> #include<algorithm> #define ll long long using namespace std; inline char gc(){ static char now[1<<16],*S,*T; if(S==T){T=(S=now)+fread(now,1,1<<16,stdin);if(T==S) return EOF;} return *S++; } inline int read(){ int x=0,f=1;char ch=gc(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();} while(isdigit(ch)) x=x*10+ch-'0',ch=gc(); return x*f; } const int N=1e5+10; struct node{ int y,next; }data[N<<1]; int num,h[N],last[N],c[N],s[N],n;ll ans; inline void insert1(int x,int y){ data[++num].y=y;data[num].next=h[x];h[x]=num; } inline void dfs(int x){ c[x]=++num; for (int i=h[x];i;i=data[i].next) dfs(data[i].y); } inline void add(int x,int v){while(x<=n+1) s[x]=max(s[x],v),x+=x&-x;} inline int query(int x){static int tmp; tmp=0;while(x) tmp=max(tmp,s[x]),x-=x&-x;return tmp; } int main(){ //freopen("bzoj5158.in","r",stdin); n=read(); for (int i=1;i<=n;++i){ static int x;x=read(); insert1(last[x-1],i);last[x]=i; }num=-1;dfs(0); //for (int i=1;i<=n;++i) printf("%d ",c[i]); for (int i=n;i;--i){static int tmp; tmp=query(c[i])+1;ans+=tmp;add(c[i],tmp); } printf("%lld\n",ans); return 0; }
转载请注明原文地址: https://www.6miu.com/read-1450063.html

最新回复(0)