別問我爲何過去一週了才寫博客。。。node
沒有作出最後一題,是我失了智。數組
這一週原本說好的計組考試結果收到的通知是計網,可憐我計組複習了兩週,計網徹底沒有複習(預習)。優化
週四終於考完本學期第一場考試,結果咱們班的現場答卷狀況讓咱們信安教研室主任勃然大怒。。spa
今天上午8點半要去實驗室作密碼學課程設計,結果睡過頭被室友鎖宿舍一上午,,,因而,,因而在宿舍頹廢了一天。不行,我這麼好的學生怎麼能頹廢呢,好吧,是個人良心在痛,晚上拉上室友圖書館複習,準備明天的校賽!設計
前三場都沒打好,只能靠這場碰碰運氣看看能不能晉級。額,維森莫是前三場,初賽前兩場去西安參加邀請賽被秦龍酒店坑了一把(去過都知道)。因此從第四場開始打的。code
急於過題覺得只有三種狀況,結果WA了一發,仔細看題原來是我失了智。這麼久沒打代碼,遇到這種shabi模擬題居然有點焦頭爛額。blog
int main() { int n; while(~scanf("%d",&n)) { printf("+-----+\n"); if(n<20) printf("| E|\n"); else if(n>=20&&n<60) printf("|- E|\n"); else if(n>=90) printf("|- 4G|\n"); else printf("|- 3G|\n"); n-=20; for(int i=2; i<=5; i++) { if(n>=20) { printf("|"); for(int j=1; j<=i; j++) printf("-"); for(int j=i+1; j<=5; j++) printf(" "); printf("|\n"); } else printf("| |\n"); n-=20; } printf("+-----+\n"); } return 0; }
這麼簡單的題我居然直接想到shabi搜索上去了,結果初始值沒賦好還WA了兩發,啊啊啊,石樂志。ip
額,中文題就不介紹題意了。ci
const int N=1e6+5; int a[55][55],x[55],y[55],vis[55][55]; int dir[5][2]= {{-1,0},{1,0},{0,1},{0,-1}}; int n,k,m; struct node { int x,y; }; int bfs(int i,int j) { memset(vis,0,sizeof(vis)); node tmp; tmp.x=x[i]; tmp.y=y[i]; queue<node>q; vis[x[i]][y[i]]=0; q.push(tmp); while(!q.empty()) { tmp=q.front(); q.pop(); if(tmp.x==x[j]&&tmp.y==y[j]) break; for(int i=0; i<4; i++) { int xx=tmp.x+dir[i][0]; int yy=tmp.y+dir[i][1]; if(a[xx][yy]&&!vis[xx][yy]) { vis[xx][yy]=vis[tmp.x][tmp.y]+1; node tmpp; tmpp.x=xx; tmpp.y=yy; q.push(tmpp); } } } return vis[x[j]][y[j]]; } int main() { while(~scanf("%d%d",&n,&k)) { memset(a,0,sizeof(a)); int h; for(int i=1; i<=n; i++) { scanf("%d",&h); for(int j=1; j<=h; j++) a[i][j]=1; } scanf("%d",&m); for(int i=1; i<=m; i++) scanf("%d%d",&x[i],&y[i]); int ans=0; for(int i=1; i<=m; i++) for(int j=i+1; j<=m; j++) { if(bfs(i,j)<=k) { ans++; // printf("%d %d\n",i,j); } } printf("%d\n",ans); } return 0; }
看了這個題的數據範圍我才意識到本身被簡單版的數據範圍坑了,明明有更簡單的方法卻往復雜的方向走。。
結合題意觀察到橫向距離是不變的(兩棟樓橫座標之差),而縱向距離只需枚舉看是否有一條通道鏈接這兩棟樓,咱們開個數組標記一下就行了。關鍵要看到H最多才20,即空間複雜度1e5*20。枚舉任意兩棟樓而後暴力判斷便可。
const int N=2e5+5; int a[N][21],x[2001],y[2001],vis[N][20],h[N]; int n,k,m; int main() { while(~scanf("%d%d",&n,&k)) { memset(a,0,sizeof(a)); memset(vis,0,sizeof(vis)); h[0]=0; for(int i=1; i<=n; i++) { for(int j=1; j<=h[i-1]; j++) vis[i][j]+=a[i-1][j]; scanf("%d",&h[i]); for(int j=1; j<=h[i]; j++) { a[i][j]=1; vis[i][j]++; } } scanf("%d",&m); for(int i=1; i<=m; i++) scanf("%d%d",&x[i],&y[i]); int ans=0; for(int i=1; i<=m; i++) for(int j=i+1; j<=m; j++) { int tmp=abs(x[j]-x[i]); int h2=max(y[i],y[j]); int f=y[j]+y[i]-2; for(int l=2;l<=h2;l++) if(abs(vis[x[i]][l]-vis[x[j]][l])==tmp) f=min(f,abs(y[i]-l)+abs(y[j]-l)); tmp+=f; if(tmp<=k) ans++; } printf("%d\n",ans); } return 0; }
仍是同樣的題意,只不過數據範圍變了,注意到和中等版本不一樣的是這個題的核心辦公室可達2e5。那麼真正有趣的來了,暴力枚舉任意兩棟樓確定是不行的,那麼怎麼優化呢。我居然想到公共祖先問題上,結果發現解決不了。。套路,又是這個套路,咱們觀察到,從左往右只要咱們知道了第一棟樓可行解的範圍也就是右端點的位置,那麼第二棟樓不就在第一棟樓的基礎上再往右貪心便可。大牛可能一眼就明白一個滑動窗便可,然而我等菜鳥居然直接想到二分上了,枚舉左端點二分右端點,我怕會超時居然用上次在玲瓏杯學到的一個技巧倍增優化。嗯,越想感受越對,在這條路上一去不復返。。到最後也沒寫出來,好吧,是我菜。講道理,這個二分套路應該是能夠的。不過犯了關鍵錯誤是咱們確定要對這些點排序,咱們想到的是按xy排序,後來發現應該按x+y的大小排序,舉個例子就懂了,(1,5)到(2,18)和(3,1)。因此應該按x+y排序。清醒後才發現能夠直接尺取貪心。。今天寫了一波,感受過TLE,然而過了,,,,過了。。。核心判斷仍是和第二題同樣。
const int N=2e5+5; int a[N][21],vis[N][21],h[N]; int n,k,m; struct node { int x,y; } b[N]; int cmp(node a,node b) { return a.x+a.y<b.x+b.y; /* 不能這樣排序,(1,5)到(2,18)和(3,1)之間的距離是不一樣的 if(a.x!=b.x) return a.x<b.x; return a.y<b.y; */ } int judge(int i,int j) { int tmp=abs(b[j].x-b[i].x); int h2=max(b[i].y,b[j].y); int f=b[j].y+b[i].y-2; for(int l=2; l<=h2; l++) if(abs(vis[b[i].x][l]-vis[b[j].x][l])==tmp) f=min(f,abs(b[i].y-l)+abs(b[j].y-l)); tmp+=f; return tmp<=k; } int main() { while(~scanf("%d%d",&n,&k)) { memset(a,0,sizeof(a)); memset(vis,0,sizeof(vis)); h[0]=0; ll ans=0; for(int i=1; i<=n; i++) { for(int j=1; j<=h[i-1]; j++) vis[i][j]+=a[i-1][j]; scanf("%d",&h[i]); for(int j=1; j<=h[i]; j++) { a[i][j]=1; vis[i][j]++; } } scanf("%d",&m); for(int i=0; i<m; i++) scanf("%d%d",&b[i].x,&b[i].y); sort(b,b+m,cmp); int j=0; for(int i=0; i<m; i++) { while(j<m&&judge(i,j)) j++;//貪心尺取 int k=j; while(k>=m||!judge(i,k)) k--;//回退,這個操做原本覺得會超時。看來數據不強 ans+=k-i; } printf("%lld\n",ans);//注意數據範圍 } return 0; }