第一題:完美序列
題目描述:給你一個長度爲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 @_@)