85,rk29,原地爆炸。。。T2寫錯一個變量名掛掉85分。。T1正解被數據範圍欺騙。。。T3三分到考試結束還在ce。。。
T1.
考試時先打了個n^3,想了想剪了個枝,變成n^2,即枚舉每個數字與其位置所形成的區間。
我們考慮優化,對於每個第i個位置形成的區間我們有i+a[i]=j+a[j]才能使j位置的數字旋轉後正位。於是我們可以用主席樹維護每個位置i+a[i]的前綴和,每次O(log)求解,總複雜度
O(nlogn)(然後我就被數據範圍騙了25分)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,a[2000005],sum[2000005],maxx,res,tot,r[2200005],tt; 4 struct tree{ 5 int sum,lc,rc; 6 }c[20000005]; 7 void insert(int &x,int l,int r,int pos) 8 { 9 c[++tt]=c[x],x=tt; 10 if(l==r){ 11 c[x].sum++; 12 return; 13 } 14 int mid=(l+r)>>1; 15 if(pos<=mid)insert(c[x].lc,l,mid,pos); 16 else insert(c[x].rc,mid+1,r,pos); 17 } 18 int query(int x,int l,int r,int pos){ 19 if(l==r){ 20 return c[x].sum; 21 } 22 int mid=(l+r)>>1; 23 if(pos<=mid)return query(c[x].lc,l,mid,pos); 24 return query(c[x].rc,mid+1,r,pos); 25 } 26 inline int read() 27 { 28 int x=0,f=1; 29 char ch=getchar(); 30 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 31 while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); 32 return x*f; 33 } 34 int main(){ 35 // freopen("t.in","r",stdin); 36 // freopen("5.out","w",stdout); 37 n=read(); 38 for(int i=1;i<=n;i++) 39 { 40 scanf("%d",&a[i]),sum[i]=sum[i-1]+(a[i]==i); 41 r[i]=r[i-1]; 42 insert(r[i],1,n+n,i+a[i]); 43 } 44 if(sum[n]==n){cout<<n<<endl;return 0;} 45 for(int i=1;i<=n;i++) 46 { 47 if(a[i]==i)continue; 48 int l=i,rr=a[i]; 49 if(l>rr)swap(l,rr); 50 int ans=query(r[rr],1,n+n,i+a[i])-query(r[l-1],1,n+n,i+a[i])+sum[n]-sum[rr]+sum[l-1]; 51 res=max(res,ans); 52 } 53 printf("%d\n",res); 54 return 0; 55 }
T2.
其實就是個建圖跑最短路,但是由於我懶得建圖,直接打了個BFS(本質上就是堆優化dij)
對於每個點,我們可以考慮它走到四個方向上的牆的最短距離就是它到離它最近的牆的距離,於是我們可以處理出這個東西,然後直接搜就行了,然而考試時寫錯一個變量名,
分數從95->10(關鍵是我當時一遍過三個樣例,然後就扔了)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int zx[4]={1,-1,0,0},zy[4]={0,0,-1,1}; 4 int n,m,tx[505][505][4],ty[505][505][4],clo[505][505]; 5 char a[505][505]; 6 bool v[505][505],vv[505][505][4]; 7 struct st{ 8 int x,y,tot; 9 friend bool operator < (st a,st b){ 10 return a.tot>b.tot; 11 } 12 }; 13 priority_queue<st>g; 14 void dfs(int x,int y,int k){ 15 vv[x][y][k]=true; 16 int ax=x+zx[k],ay=y+zy[k]; 17 if(a[ax][ay]!='#') 18 dfs(ax,ay,k),tx[x][y][k]=tx[ax][ay][k],ty[x][y][k]=ty[ax][ay][k]; 19 else tx[x][y][k]=x,ty[x][y][k]=y; 20 } 21 void init(){ 22 for(int i=1;i<=n;i++) 23 for(int j=1;j<=m;j++) 24 { 25 if(a[i][j]!='#'){ 26 for(int k=0;k<=3;++k) 27 { 28 if(!vv[i][j][k]) 29 dfs(i,j,k); 30 } 31 clo[i][j]=min(min(min(abs(tx[i][j][0]-i)+abs(ty[i][j][0]-j),abs(tx[i][j][1]-i)+abs(ty[i][j][1]-j)), 32 abs(tx[i][j][2]-i)+abs(ty[i][j][2]-j)),abs(tx[i][j][3]-i)+abs(ty[i][j][3]-j))+1; 33 } 34 } 35 } 36 void bfs(){ 37 memset(v,0,sizeof(v)); 38 while(!g.empty()) 39 { 40 st aa=g.top();g.pop(); 41 const int x=aa.x,y=aa.y,t=aa.tot; 42 if(v[x][y])continue; 43 v[x][y]=true; 44 if(a[x][y]=='F') 45 { 46 printf("%d\n",t); 47 exit(0); 48 } 49 for(int i=0;i<=3;i++){ 50 int ax=x+zx[i],ay=y+zy[i]; 51 if(a[ax][ay]!='#') 52 g.push((st){ax,ay,t+1}); 53 } 54 g.push((st){tx[x][y][0],ty[x][y][0],t+clo[x][y]}); 55 g.push((st){tx[x][y][1],ty[x][y][1],t+clo[x][y]}); 56 g.push((st){tx[x][y][2],ty[x][y][2],t+clo[x][y]}); 57 g.push((st){tx[x][y][3],ty[x][y][3],t+clo[x][y]}); 58 59 } 60 puts("no"); 61 exit(0); 62 } 63 int main(){ 64 scanf("%d%d",&n,&m); 65 for(int i=1;i<=n;i++) 66 { 67 scanf("%s",a[i]+1); 68 for(int j=1;j<=m;j++) 69 if(a[i][j]=='C') 70 g.push((st){i,j,0}); 71 } 72 init(); 73 bfs(); 74 }
T3.
好題。
正解:首先在每根柱子作爲最高時,總收益對於其高度爲一個單峯函數(感性理解一下),於是我們可以枚舉最高柱,然後三分其高度,每次O(n)求解,總複雜度O(n^2logh),考試時打的這個到結束還在ce(說起來你可能不信,我調了20min的ce)。
然後我們考慮優化,之前的瓶頸在於統計答案,那麼我們可以考慮柱子的性質:
在第i根柱子作爲最高時,對於j<i,的柱子,我們有hj-j=hi-i,對於j>i,我們有hj+j=hi+i,因此我們可以維護這兩個東西,用兩個樹狀數組。將這兩個東西sort一遍,同時存下每根柱子在這兩種情況下的排名,在枚舉每根柱子作爲最高柱時,三分其高度,同時二分出左右中第一個需要增長的柱子的排名,樹狀數組查詢它的前綴和,分類加加減減,即可O(log)得出每一組解。總複雜度O(nlognlogh)
亂搞:
1.騙分:因爲答案隨高度單峯,我們可以猜想,答案是否也隨位置單峯?三分套三分,複雜度與正解相同,但會出錯,且會T(常數略大),期望得分80。
2.優化騙分:我們想辦法將上面那個東西優化,當一根柱子被確定爲最高柱時,其最優高度也就確定,是將所有柱子根據斜率增加高度後的中位數(i作爲最高,i+1,i-1增加1,i+2,i-2增加2,以此類推),因此複雜度變爲O(nlogn),不會T,但會wa,期望得分90。
3.牛逼:模擬退火!利用上面的那個東西O(n)查詢每根柱子的最優解,利用退火隨機求解。由於本題數據不好造,答案隨位置基本單峯,或峯值很少,退火可以在很短時間內求出很優的解。複雜度玄學,期望得分100。