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 ",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 ",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。