1. 程式人生 > >【TMD模擬賽】上低音號 鏈表

【TMD模擬賽】上低音號 鏈表

枚舉 true size line 以及 point eof print 邊界

這道題一看有兩個出發現點,一枚舉點去找邊界,想了一會就Pass了...,二是枚舉相框,我們最起碼枚舉兩個邊界,然後發現平行邊界更好處理,然而仍然只有30分,這個時候就來到了鏈表的神奇應用,我們枚舉上界u,下界d在u的基礎之上從下往上枚舉,我們每次枚舉上界的開始就把上界以下的點建成鏈表(它的形狀大概是在從左到右的基礎上對於同一列的從上倒下,就是蛇形),然後讓下屆去逼近並結算答案,十分巧妙。

關於鏈表:大概有單向鏈表,雙向鏈表,以及循環鏈表,他們作為數據結構的應用十分狹窄,只有在特定情境下才有大用處,所以在oi層面只有雙向鏈表有一定用處,他的刪除在記錄位置的前提下是O(1)的,查詢O(n),插入的話主要是找到合適的位置(前驅),剩下的就是O(1)了。

#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=3065;
int pre[N],next[N],mem[N][N],last[N];
int line,column,n,k;
LL ans;
bool ex[N];
struct Point{int x,y;}p[N];
inline bool comp(Point a,Point b){return a.y<b.y||(a.y==b.y&&a.x<b.x);}
int main(){ scanf("%d%d%d%d",&line,&column,&n,&k); for(int i=1;i<=n;++i) scanf("%d%d",&p[i].x,&p[i].y); std::sort(p+1,p+n+1,comp); for(int i=1;i<=n;++i) mem[p[i].x][++mem[p[i].x][0]]=i; for(int u=1;u<=n;++u){ int pr=0;memset(ex,0,sizeof(ex));
for(int i=u;i<=n;++i) for(int j=1;j<=mem[i][0];++j) ex[mem[i][j]]=true,last[mem[i][j]]=line; for(int i=1;i<=n;++i) if(ex[i])pre[i]=pr,next[pr]=i,pr=i; pre[n+1]=pr,next[pr]=n+1; for(int i=2;i<=k;++i) pre[n+i]=n+i-1,next[n+i-1]=n+i; for(int d=n;d>=u;--d){ for(int i=1;i<=mem[d][0];++i){ int nk=mem[d][i]; for(int j=1;j<=k;++j)nk=next[nk]; if(next[mem[d][i]]<=n&&p[next[mem[d][i]]].x!=d) ans+=(LL)(last[next[mem[d][i]]]-d+1)* (p[next[mem[d][i]]].y-p[pre[next[mem[d][i]]]].y)* (nk>n?0:column-p[nk].y+1), last[next[mem[d][i]]]=d-1; nk=pre[nk], ans+=(LL)(last[mem[d][i]]-d+1)* (p[mem[d][i]].y-p[pre[mem[d][i]]].y)* (nk>n?0:column-p[nk].y+1); for(int j=1,now=mem[d][i];j<k&&now;++j){ now=pre[now],nk=pre[nk]; if(now&&p[now].x!=d&&last[now]>=d) ans+=(LL)(last[now]-d+1)* (p[now].y-p[pre[now]].y)* (nk>n?0:column-p[nk].y+1), last[now]=d-1; } } for(int i=1;i<=mem[d][0];++i) next[pre[mem[d][i]]]=next[mem[d][i]], pre[next[mem[d][i]]]=pre[mem[d][i]]; } } printf("%lld",ans); }

【TMD模擬賽】上低音號 鏈表