1001
这题没什么坑点。、跳过....
1002
给出10万个在x轴上的线段,问最多的覆盖到同一点的线段数。
分析:将线段离散化,就有2*10w个点,其中,我们把起点权定为1,终点权定为-1。题目就转换为求最大前缀和。
比如我现在扫到了一个 -1的点,我并不在乎是哪一条线段结束了,在乎的是我线段的数量就-1了,。所以,我们标示起点终点,排序之后求最大前缀和。
我做题的时候没注意到这一点,于是把所有线段插入树状数组中,求的也是最大前缀和。
<span style="font-family:Microsoft YaHei;">#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <map>
using namespace std;
#define Maxn 1000000
int se[Maxn+50],x[Maxn],y[Maxn];
int c[Maxn+50];
int n,top;
int lowbit(int x){
return x&(-x);
}
void change(int k,int delt){
while (k<=top){
c[k]+=delt;
k+=lowbit(k);
}
}
int getsum(int k){
int sum=0;
while (k>=1){
sum+=c[k];
k-=lowbit(k);
}
return sum;
}
int get(int l,int r,int x){
int pos=lower_bound(se+l,se+r+1,x)-se;
return pos;
}
int main(){
int cases;
scanf("%d",&cases);
for (int cas=1;cas<=cases;cas++){
memset(c,0,sizeof(c));
scanf("%d",&n);
top=0;
for (int i=1;i<=n;i++){
scanf("%d%d",&x[i],&y[i]);
se[++top]=x[i];
se[++top]=y[i];
}
sort(se+1,se+1+top);
for (int i=1;i<=n;i++){
change(get(1,top,x[i]),1);
change(get(1,top,y[i])+1,-1);
}
/*for (int i=1;i<=top;i++) cout<<se[i]<<' ';
cout<<endl;
for (int i=1;i<=n;i++) cout<<get(1,top,i)<<' ';
cout<<endl;*/
int ans=0;
for (int i=1;i<=n;i++){
ans=max(ans,getsum(get(1,top,x[i])));
ans=max(ans,getsum(get(1,top,y[i])));
}
printf("%d\n",ans);
}
return 0;
}</span>
题意:给出两个长度为 n 的数组,有m次对应位置交换的机会a[i] 和b[i]交换 ,问最长递增子序列最长是多少。
比赛的时候想出了dp方程却放弃了,用了一个dp[i][2] 和 cnt[i][2]来做,实际上是不正确的,比如我现在到了 i =3的位置,我记录的是到了 dp[i][0]用了m次魔法的最大值。可实际上我dp[3][0]用了0次魔法才能取得全局的最优值,所以这个做法是不行的。
分析:dp方程一开始很好想,dp[i][j][2]表示 到 i 个位置用了 j 个魔法停留位置在 a 或 b 的最长上升子序列是多少。可是考虑时间复杂度去高达n*n*m,而实际上dp的转移方程是取 dp[k][j][0/1] 或者dp[k][j-1][0/1]的最大值,于是使用树状数组求1~n的最大值,可以使得转移的复杂度降低为log n。于是我们先离散数据(这个很关键,离散数据的大小涉及到树状数组的大小,并且要注意 树状数组从1开始),以n划分阶段,维护m个树状数组(注意不要影响当前阶段的最优值,考虑下循环顺序或者一起最后更新),答案取最优值即可。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
#define Maxn 1050
int a[Maxn],b[Maxn],se[2*Maxn];
int dp[Maxn][Maxn][2],c[Maxn][2*Maxn];
int top;
int lowbit(int x){
return x&(-x);
}
void change(int j,int k,int delt){
while (k<=top){
c[j][k]=max(c[j][k],delt);
k+=lowbit(k);
}
}
int getmax(int j,int k){
int Max=0;
while (k>=1){
Max=max(Max,c[j][k]);
k-=lowbit(k);
}
return Max;
}
int get(int x){
int pos=lower_bound(se+1,se+1+top,x)-se;
return pos;
}
int main(){
int cases,n,m;
scanf("%d",&cases);
for (int cas=1;cas<=cases;cas++){
memset(c,0,sizeof(c));
scanf("%d%d",&n,&m);
top=0;
for (int i=1;i<=n;i++){
scanf("%d%d",&a[i],&b[i]);
se[++top]=a[i];se[++top]=b[i];
}
sort(se+1,se+1+top);
top=unique(se+1,se+top+1)-se-1;
for (int i=1;i<=n;i++){
a[i]=get(a[i]);b[i]=get(b[i]);
}
int ans=0;
for (int i=1;i<=n;i++){
for (int j=0;j<=min(i,m);j++){
dp[i][j][0]=getmax(j,a[i]-1)+1;
ans=max(ans,dp[i][j][0]);
if (j){
dp[i][j][1]=getmax(j-1,b[i]-1)+1;
ans=max(ans,dp[i][j][1]);
}
}
for (int j=0;j<=min(i,m);j++){
change(j,a[i],dp[i][j][0]);
if (j) change(j,b[i],dp[i][j][1]);
}
}
printf("%d\n",ans);
}
return 0;
}
对dp转移优化不理解的可以先试试poj1631,以下给出Poj1631的代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
#define Maxn 40050
using namespace std;
int a[Maxn],dp[Maxn],c[Maxn];
int n;
inline int lowbit(int x){
return x&(-x);
}
void change(int k,int delt){
while (k<=n){
c[k]=max(c[k],delt);
k+=lowbit(k);
}
}
int getmax(int k){
int Max=0;
while (k>=1){
Max=max(Max,c[k]);
k-=lowbit(k);
}
return Max;
}
int main(){
int cases;
scanf("%d",&cases);
while (cases--){
scanf("%d",&n);
memset(c,0,sizeof(c));
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int ans=0;
for (int i=1;i<=n;i++){
dp[i]=getmax(a[i]-1)+1;
change(a[i],dp[i]);
ans=max(ans,dp[i]);
}
printf("%d\n",ans);
}
return 0;
}
1004
题解说cdq分治,我还是快补吧。