题目大意:某个大学有个2个校区,此大学有n(1<=n<=10000)个运动员,这n个运动员在每个校区都挑选了m(1<=m<=10)个拉拉队。现在每个校区(A/B)中,这m*n个拉拉队按照登记顺序说出自己支持的运动员编号和自己想排在那个位置(输入顺序,m*n*2个数字),排成一列。如果冲突则按照先来后到的顺序依次往后排。求按照A/B的两个拉拉队员站的位置,用他们支持的运动员编号形成的两个序列的最长公共子序列。这道题给4s,20组样例,运算复杂度允许10^7。

思路还是比较明确的。知识点:稀疏序列的匹配,最长上升子序列

第一步:预处理求A/B串。

这个比较好办,如果A(B)列中某君L想在第X个位置:选第X个位置给它,如果X没人占;二分枚举Y(Y>X),X到Y之间空位 = F(Y)-F(X)+1 {F(X)表示1到X之间站的人数},如果X有人站。(树状数组+二分)

第二步:比较A/B。

(最初想法,现在令f(x,y)表示A的前x个数字与B中前y个数字的匹配数,对x从前到后枚举,找B个匹配的y(最多m个),那么f(x,y')=max(f(x,y'),f(x,y-1)+1) {y<=y'<=N=m*n},这不很典型的区间置数问题了吗?solution就很容易想到:线段树。可惜TLE,搓啊!!!!)

首先感谢网上某位大神和我的队友:wonderzy.

最后答案对应的那个目标串中每个匹配的串中每个元素在源串肯定都是有一个下标。

如样例A:3 3 1 1 2 2, B: 3 2 3 1 2 1,最后答案是4,对应着3 3 1 1/3 3 1 2。

其实如果这么A中每一个元素可选在B中的位置(3 1) (3 1) (6 4) (6 4) (5 2) (5 2)。A中每个元素就对应着一个括号,标识着它的可选方案,当然这个元素也可以不选。

这样,结果不就是正好是3 1 3 1 6 4 6 4 5 2 5 2这个串最长上升子序列长度吗?

贴个代码

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=;
struct position{
int id,atx;
}pos[maxn];
int S[maxn*],*pid,N;
#define lowest(x) ((-x)&x)
int sumDown(int x){
int ret=;
while(x>){
ret += S[x];
x -= lowest(x);
}
return ret;
}
void addUp(int x,int id){
pid[x]=id;
while(x <= N){
S[x]++;
x+=lowest(x);
}
}
int findPos(int sf,int left,int right){
int x0=left-;
while(left < right){
int x=(left+right)>>;
int fx=sumDown(x);
if(x-x0 > fx-sf) right=x;
else left=x+;
}
return left;
}
void setOrder(int *A,int m,int n){
pid=A; N=m*n*;
for(int i=N;i>=;i--) S[i]=pid[i]=;
int n2=n*m;
for(int i=;i<n2;i++){
int px=pos[i].atx;
if(!pid[px]) addUp(px,pos[i].id);
else {
int sf=sumDown(px);
px=findPos(sf,px+,N);
addUp(px,pos[i].id);
}
}
int p1=,p2=;
while(p1<n2){
if(pid[p2]) pid[++p1]=pid[p2];
p2++;
}
}
int A[maxn*],B[maxn*],C[maxn*];
int pre[maxn*],head[maxn*];
int gao_LIS(int a[],int len){ //最长公共子序列
int ret=;
int b[maxn];
b[ret++]=a[];
for(int i=;i<len;i++){
int x=lower_bound(b,b+ret,a[i])-b;
if(x==ret){
b[ret++]=a[i];
}else{
b[x]=a[i];
}
}
return ret;
}
int main()
{
int cases; cin>>cases;
int n,m;
for(int cas=;cas<=cases;cas++){
scanf("%d%d",&n,&m);
int n2=m*n;
for(int i=;i<n2;i++) scanf("%d%d",&pos[i].id,&pos[i].atx);
setOrder(A,m,n);
for(int i=;i<n2;i++) scanf("%d%d",&pos[i].id,&pos[i].atx);
setOrder(B,m,n);
//for(int i=1;i<=n2;i++) printf("%d ",A[i]); cout<<endl;
//for(int i=1;i<=n2;i++) printf("%d ",B[i]); cout<<endl;
/******************************/
for(int i=m*n;i>=;i--) head[i]=;
for(int i=;i<=n2;i++)
pre[i]=head[A[i]], head[A[i]]=i;
int idx=;
for(int i=;i<=n2;i++)
for(int j=head[B[i]];j;j=pre[j])
C[idx++]=j;
//for(int i=0;i<idx;i++) cout<<C[i]<<" "; cout<<endl;
printf("Case #%d: %d\n",cas,gao_LIS(C,idx));
/******************************/
}
return ;
}

hdu3525的更多相关文章

随机推荐

  1. Android集成科大讯飞SDK语音听写及语音合成功能实现

    前言 现在软件设计越来越人性化.智能化.一些常见的输入都慢慢向语音听写方向发展,一些常见的消息提示都向语音播报发展.所以语音合成和语音听写是手机软件开发必不可少的功能.目前国内这方面做的比较好的应该是 ...

  2. 自学Android的第一个小程序(小布局、button点击事件、toast弹出)

    因为上班,学习时间有限,昨晚才根据教程写了一个小程序,今天忙里偷闲写一下如何实现的,来加深一下印象. 首先创建一个Android项目, 通过activity_xxx.xml布局文件来添加组件来达到自己 ...

  3. UIView和其子类的几个初始化函数执行的时机

    -(id)initWithFrame:(CGRect)frame - UIView的指定初始化方法; 总是发送给UIView去初始化, 除非是从一个nib文件中加载的; -(id)initWithCo ...

  4. iBatis核心框架浅析

    1.1 iBatis配置与运行 1.dal 层的dao接口实现类通常会继承SqlMapClientDaoSupport.spring容器在初始化一个dao bean实例时,通常会注入两块信息DataS ...

  5. oracle 序列

    查询序列值 select td_prodline_attr_seq.nextval from dual     查询用户建的所有序列 用户名 必须大写select SEQUENCE_OWNER,SEQ ...

  6. Fragment之一:Fragment入门

    参考自张泽华视频 Fragment是自Android3.0后引入的特性,主要用于在不同的屏幕尺寸中展现不同的内容. Fragment必须被嵌入Activity中使用,总是作为Activity的组成部分 ...

  7. 简单的JQuery top返回顶部

    一个最简单的JQuery Top返回的代码,Mark一下: HTML如下: <div id="backtop"> <a href="javascript ...

  8. 在Azure Cloud Service中部署Java Web App(2)

    接上文. 9.在进行发布之前,需要对我们的订阅做一些设置,因为默认情况下,Azure的service end指向的是Azure global的站点,如果我们要将服务发布在Azure的中国站点,需要做下 ...

  9. android 测量控件视图的方法

    在实际项目中经常要用到 测量一个控件或者视图的高,宽.然后根据这个高宽进行一些逻辑. 计算视图宽高有几种方式先简单的了解下android 视图的绘制过程会促进理解. 一.android View绘制过 ...

  10. USB mass storage协议

    这一节主要把在实现“linux模拟U盘功能”过程中的一些调试过程记录下来,并加以解析. 一.背景知识     1.USB Mass Storage类规范概述        USB 组织在univers ...