第一题:完美序列
题目描述:给你一个长度为n(1<=n<=100,000)的自然数数列,其中每一个数都小于等于10亿,现在给你一个k,表示你最多可以删去k类数。数列中相同的数字被称为一类数。设该数列中满足所有的数字相等的连续子序列被叫做完美序列,你的任务就是通过删数使得该数列中的最长完美序列尽量长。
输入格式:第一行两个整数N,K。
输出格式:最长的完美序列的长度。
成绩:30
题解:两个变量l和r表示一个区间[l,r]保证里面有<=k+1种元素,那么问题就转化为了求所有满足条件的区间[l,r]中最多的元素个数(因为只有<=k+1种元素,保留最多的一种,其他的删掉就行了23333333),这样l和r初始值都为1,元素种类<=k是r++,==k+1时l++,r++(平移区间),就可以在O(n)的时间里得到答案。(然而我的离散化O(nlogn)23333333…(⊙o⊙)…)
分析:考试时一直在想,要保证所有删除的元素都在一种元素的中间(不然浪费了2333~\(≧▽≦)/~啦啦啦)然后就把输入处理成了node(l,r,num)[l,r]中的元素都是num。
接着妄想在O(n)的时间里处理出保留每一种元素的最长完美序列(几个num相同的node中间间隔<=k种元素)然而写到一半才发现元素个数与中间node的个数并不同(居然发现的如此之晚TATTATTATTATTATTATTATTATTATTATTATTATTATTATTATTATTAT
TATTATTATTATTATTATTATTATTATTATTATTATTATTATTATTATTATTATTATTAT)
然后就随便写了个O(n^2)的(~qnq~~qnq~~qnq~~qnq~~qnq~~qnq~~qnq~~qnq~)
(…(⊙o⊙)…),其实以前做过一道题:滑动窗口就是类似的思路,然而忘了(Q_Q)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
using namespace std;
const int N=100000+10;
void getint(int&num){
char c;int flag=1;num=0;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
num*=flag;
}
struct node{
int num,w,pos;
bool operator < (const node &x)const{
return num<x.num;
}
}arr[N];
bool cmp(node a,node b){
return a.pos<b.pos;
}
int n,k,cnt,l=1,r=1,tot,ans,sum[N];
int main(){
getint(n),getint(k),k++;
for(int i=1;i<=n;i++)
getint(arr[i].num),arr[i].pos=i;
sort(arr+1,arr+n+1);
arr[0].w=arr[0].num=-1;
for(int i=1;i<=n;i++){
if(arr[i].num!=arr[i-1].num)
cnt++;
arr[i].w=cnt;
}
sort(arr+1,arr+n+1,cmp);
while(r<n){
if(!sum[arr[r].w]) tot++;
ans=max(ans,++sum[arr[r].w]);
while(tot>k){
sum[arr[l].w]--;
if(!sum[arr[l].w]) tot--;
l++;
}
r++;
}
printf("%d\n",ans);
return 0;
}
第二题:Island Travels
题目描述:FJ组织它的奶牛去海边度假。海边有N(N<=15)个岛屿,这些岛屿分布在一个R*C(1<=R,C<=50)的区域中。一个岛屿是一个由‘X’组成连通块。有的格子标有‘S’,表示浅水区,有的格子标有‘.’,表示深水区。奶牛贝西可以从任意一个岛屿出发,它要遍历所有的岛屿,它可以游泳通过浅水区。求贝西游泳的最短的距离——每一次经过的S的个数之和。同一个‘S’可以经过多次,但是每次都要记入距离。
输入:第一行R,C。接下来R行C列,每个字符可能是‘X’,‘S’,‘.’。
输出:贝西游泳的最短距离。
成绩:36
题解:先bfs出所有岛屿,编号,然后用spfa+flody求出两两间距离(S的个数)。然后dp,一个二进制数i表示当前状态(二进制第a为1表示已经过第a个岛屿),j表示当前在第j个岛屿,k表示上一个岛屿,dp[i][j]=min(dp[i的第j位为0][k]+dis[k][j])(这种形式符号语言不好表达,代码是倒着写的dp[i|(1<<(j-1)][j]=min(dp[i][k]+map[k][j]);)
分析:没有看到岛屿上限,然后根本没有考虑2进制(其实看到了应该也想不出来23333)
最后自暴自弃写了个bfs+dfs大暴力有36分全是惊喜~\(≧▽≦)/~啦啦啦
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int N=60;
const int inf=0x3f3f3f3f;
void getint(int&num){
char c;int flag=1;num=0;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
num*=flag;
}
struct node{
int x,y;
node(){}
node(int a,int b){
x=a,y=b;
}
}tmp;
queue <node> q;
int R,C,cnt,end,ans=inf,dd[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
int flag[N][N],dis[N][N][N],map[N][N],dp[1<<16][20];
bool vis[N][N];
char s[N][N];
bool inarea(int x,int y){
if(x<1||x>R||y<1||y>C) return 0;
else return 1;
}
void bfs(int x,int y,int val){
q.push(node(x,y));
while(!q.empty()){
tmp=q.front(),q.pop();
for(int i=0;i<4;i++){
int xx=tmp.x+dd[i][0],yy=tmp.y+dd[i][1];
if(inarea(xx,yy)&&s[xx][yy]=='X'&&!flag[xx][yy])
flag[xx][yy]=val,q.push(node(xx,yy));
}
}
}
void spfa(int x,int y,int pos){
while(!q.empty()) q.pop();
q.push(node(x,y)),dis[pos][x][y]=0;
while(!q.empty()){
tmp=q.front(),q.pop();
vis[tmp.x][tmp.y]=0;
for(int i=0;i<4;i++){
int xx=tmp.x+dd[i][0],yy=tmp.y+dd[i][1];
if(inarea(xx,yy)&&s[xx][yy]!='.')
if(dis[pos][xx][yy]>dis[pos][tmp.x][tmp.y]+(s[xx][yy]=='S')){
dis[pos][xx][yy]=dis[pos][tmp.x][tmp.y]+(s[xx][yy]=='S');
if(!vis[xx][yy])
vis[xx][yy]=1,q.push(node(xx,yy));
}
}
}
for(int i=1;i<=R;i++)
for(int j=1;j<=C;j++){
if(pos==flag[i][j]) continue;
if(map[pos][flag[i][j]]>dis[pos][i][j])
map[pos][flag[i][j]]=map[flag[i][j]][pos]=dis[pos][i][j];
}
}
int main(){
getint(R),getint(C);
for(int i=1;i<=R;i++)
scanf("%s",s[i]+1);
for(int i=1;i<=R;i++)
for(int j=1;j<=C;j++)
if(s[i][j]=='X'&&!flag[i][j])
bfs(i,j,flag[i][j]=++cnt);
memset(dis,0x3f,sizeof(dis));
memset(map,0x3f,sizeof(map));
for(int i=1;i<=R;i++)
for(int j=1;j<=C;j++)
if(s[i][j]=='X')
spfa(i,j,flag[i][j]);
for(int k=1;k<=cnt;k++)
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
memset(dp,0x3f,sizeof(dp));
for(int i=0;i<cnt;i++) dp[1<<i][i]=0;
for(int i=0;i<(1<<cnt);i++)
for(int j=0;j<cnt;j++)
if((i>>j)&1)
for(int k=0;k<cnt;k++)
dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+map[j+1][k+1]);
int end=(1<<cnt)-1;
for(int i=1;i<=cnt;i++)
ans=min(ans,dp[end][i]);
printf("%d\n",ans);
return 0;
}
第三题:座位
题目描述:一个餐馆中有n个座位,这些座位排成一条直线,从左到右编号为1,2,……n。这天有m个事件发生。事件有两种:一种是有新的订单,需要p个连续的座位。如果可以满足,就找到编号最小的一组给他们坐,如果不能满足,则要拒绝这个订单;一种是区间[a,b]的人全部离开。请把餐馆的老板统计有多少个订单不能满足。
输入:第一行有两个整数,N,M。(1<=N<=500000,1<=m<=300000)。接下来从第2行到第M行,表示有M个事件。要么是“A p”的形式,表示有订单,需要p个连续座位,要么是”L a b”的形式,表示区间[a,b]的人离开。
输出:需要拒绝的订单的数量。
成绩:AC
题解:线段树维护一个区间的:总最大,最大前缀,最大后缀,最大连续的第一个(这个不是必须,但是维护了会快一些)
分析:原题~\(≧▽≦)/~啦啦啦
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
using namespace std;
#define lson num<<1
#define rson num<<1|1
const int MAXN=500000+10;
struct node{
int l,r,sum,pre,lst,fir;
node(){}
node(int a,int b,int c,int d){
l=a,r=b,sum=pre=lst=c,fir=d;
}
}tree[MAXN<<2];
int n,T,l,r,ans;
char op[3];
void getint(int&num){
char c;int flag=1;num=0;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
num*=flag;
}
void build(int num,int l,int r){
tree[num]=node(l,r,r-l+1,l);
if(l==r) return;
int mid=(l+r)>>1;
build(lson,l,mid),build(rson,mid+1,r);
}
void push_down(int num){
if(tree[num].sum==tree[num].r-tree[num].l+1){
tree[lson].pre=tree[lson].lst=tree[lson].sum=tree[lson].r-tree[lson].l+1;
tree[rson].pre=tree[rson].lst=tree[rson].sum=tree[rson].r-tree[rson].l+1;
tree[lson].fir=tree[lson].l,tree[rson].fir=tree[rson].l;
}
else if(tree[num].sum==0){
tree[lson].pre=tree[lson].lst=tree[lson].sum=tree[lson].fir=0;
tree[rson].pre=tree[rson].lst=tree[rson].sum=tree[rson].fir=0;
}
}
void update(int num){
if(tree[lson].sum>=tree[rson].sum)
tree[num].sum=tree[lson].sum,tree[num].fir=tree[lson].fir;
else tree[num].sum=tree[rson].sum,tree[num].fir=tree[rson].fir;
if(tree[num].sum<tree[lson].lst+tree[rson].pre)
tree[num].sum=tree[lson].lst+tree[rson].pre,tree[num].fir=tree[lson].r-tree[lson].lst+1;
if(tree[lson].pre==tree[lson].r-tree[lson].l+1)
tree[num].pre=tree[lson].pre+tree[rson].pre;
else tree[num].pre=tree[lson].pre;
if(tree[rson].lst==tree[rson].r-tree[rson].l+1)
tree[num].lst=tree[rson].lst+tree[lson].lst;
else tree[num].lst=tree[rson].lst;
}
void cover(int num,int l,int r,int val){
if(l>tree[num].r||r<tree[num].l) return;
else if((tree[num].sum==tree[num].r-tree[num].l+1&&!val)||(val&&!tree[num].sum)) return ;
else if(tree[num].l>=l&&tree[num].r<=r){
if(val==0) tree[num].pre=tree[num].lst=tree[num].sum=tree[num].r-tree[num].l+1,tree[num].fir=tree[num].l;
else tree[num].fir=tree[num].pre=tree[num].lst=tree[num].sum=0;
}
else push_down(num),cover(lson,l,r,val),cover(rson,l,r,val),update(num);
}
int pos=MAXN;
void cal(int num,int len){
pos=min(tree[num].fir,pos);
if(tree[lson].sum>=len) cal(lson,len);
else if(tree[lson].lst + tree[rson].pre >= len)
pos=min(tree[lson].r-tree[lson].lst+1,pos);
else if(tree[rson].sum>=len) cal(rson,len);
}
int main(){
freopen("seat.in","r",stdin);
freopen("seat.out","w",stdout);
getint(n),getint(T);
build(1,1,n);
while(T--){
scanf("%s",op);
if(op[0]=='A'){
getint(l);
if(tree[1].sum>=l)
cal(1,l),cover(1,pos,pos+l-1,1),pos=MAXN;
else ans++;
}
else getint(l),getint(r),cover(1,l,r,0);
}
printf("%d\n",ans);
}
总结:第一题卡了很久很久很久很久很久很久很久很久很久很久很久很久很久很久很久很久
(重点是还是没有想出来2333333 ~qnq~),第二题如果看到了岛屿上限,其实可以很自然地想到二进制,至少就算是没有其他思路,优化一下暴搜,也应该可以多拿一点分的(233333 Q_Q @_@)