T1:旋转子段
性质1:每个点有且只有一个旋转中心能使其旋转后变为固定点
性质2:最优旋转子段的两端点至少有一个在旋转后成为固定点
根据上述性质,可以发现,可能成为最优旋转子段的区间只有n个,枚举就好了

1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<vector> 7 #define ll long long 8 using namespace std; 9 const int MAXN=1000005,INF=0x3f3f3f3f; 10 int n,v[MAXN],pos[MAXN],ans,sum[MAXN]; 11 vector<int> w[MAXN*2]; 12 int get_num(int l,int r,int x) { 13 if(!w[x].size()) return 0; 14 int tl=lower_bound(w[x].begin(),w[x].end(),l)-w[x].begin(); 15 int tr=upper_bound(w[x].begin(),w[x].end(),r)-w[x].begin(); 16 if(tl>tr) return 0; 17 return tr-tl; 18 } 19 int main() { 20 scanf("%d",&n); 21 for(int i=1;i<=n;i++) { 22 scanf("%d",&v[i]); 23 pos[i]=v[i]+i-1; 24 w[pos[i]].push_back(i); 25 sum[i]=sum[i-1]; 26 if(v[i]==i) ++sum[i]; 27 } 28 ans=sum[n]; 29 for(int i=1;i<=n;i++) { 30 if(i==v[i]) continue; 31 int tmp=0; 32 int r=pos[i]-i+1,l=i; 33 if(l>r) swap(l,r); 34 tmp=get_num(l,r,pos[i]); 35 tmp+=sum[l-1]+sum[n]-sum[r]; 36 ans=max(ans,tmp); 37 } 38 printf("%d ",ans); 39 return 0; 40 }
T2:走格子
建图,对于每个空点,连出两种边
一种表示向四个方向走一步
即:向相邻的四个点连距离为1的边
另一种表示向四个方向发射传送门,并走向四个方向中最近的墙,然后进行传送
即:向四个方向墙的前一格连距离为最近的墙距离+1的边
然后跑最短路即可

1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<vector> 7 #include<queue> 8 #define ll long long 9 using namespace std; 10 const int MAXN=1005,INF=0x3f3f3f3f; 11 int n,m,ans,bx,by,ex,ey; 12 int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0}; 13 char mp[MAXN][MAXN]; 14 bool vis[MAXN][MAXN]; 15 int to[MAXN*MAXN*8],nxt[MAXN*MAXN*8],dis[MAXN*MAXN*8],tot,h[MAXN*MAXN]; 16 void add(int x,int y,int z) { 17 to[++tot]=y; 18 nxt[tot]=h[x]; 19 dis[tot]=z; 20 h[x]=tot; 21 } 22 bool noans() { 23 queue<pair<int,int> > q; 24 q.push(make_pair(bx,by)); 25 while(!q.empty()) { 26 int x=q.front().first,y=q.front().second; q.pop(); 27 if(x==ex&&y==ey) return 0; 28 for(int i=0;i<4;i++) { 29 int nx=x+dx[i],ny=y+dy[i]; 30 if(mp[nx][ny]=='#') continue; 31 if(vis[nx][ny]) continue; 32 vis[nx][ny]=1; 33 q.push(make_pair(nx,ny)); 34 } 35 } 36 return 1; 37 } 38 int mi[MAXN][MAXN],ver[MAXN][MAXN][4]; 39 int get_dis(int x,int y,int id) { 40 int ret=0,lstx=x,lsty=y; 41 while(mp[x][y]!='#') x=x+dx[id],y=y+dy[id],++ret; 42 ver[lstx][lsty][id]=(x-dx[id]-1)*m+y-dy[id]; 43 return ret; 44 } 45 void build() { 46 memset(mi,0x3f,sizeof(mi)); 47 for(int i=1;i<=n;i++) 48 for(int j=1;j<=m;j++) 49 if(mp[i][j]!='#') for(int k=0;k<4;k++) 50 mi[i][j]=min(mi[i][j],get_dis(i,j,k)); 51 for(int i=1;i<=n;i++) 52 for(int j=1;j<=m;j++) 53 if(mp[i][j]!='#') for(int k=0;k<4;k++) 54 if(ver[i][j][k]!=(i-1)*m+j) 55 add((i-1)*m+j,ver[i][j][k],mi[i][j]); 56 for(int i=1;i<=n;i++) 57 for(int j=1;j<=m;j++) 58 if(mp[i][j]!='#') for(int k=0;k<4;k++) 59 if(mp[i+dx[k]][j+dy[k]]!='#') 60 add((i-1)*m+j,(i+dx[k]-1)*m+j+dy[k],1); 61 } 62 int d[MAXN*MAXN]; 63 bool ins[MAXN*MAXN]; 64 void SPFA() { 65 memset(d,0x3f,sizeof(d)); 66 d[(bx-1)*m+by]=0; 67 queue<int> q; q.push((bx-1)*m+by); 68 ins[(bx-1)*m+by]=1; 69 while(!q.empty()) { 70 int u=q.front(); q.pop(); 71 ins[u]=0; 72 for(int i=h[u];i;i=nxt[i]) { 73 int v=to[i]; 74 if(d[v]>d[u]+dis[i]) { 75 d[v]=d[u]+dis[i]; 76 if(!ins[v]) q.push(v),ins[v]=1; 77 } 78 } 79 } 80 ans=d[(ex-1)*m+ey]; 81 } 82 int main() { 83 scanf("%d%d",&n,&m); 84 for(int i=1;i<=n;i++) scanf("%s",mp[i]+1); 85 for(int i=1;i<=n;i++) 86 for(int j=1;j<=m;j++) { 87 if(mp[i][j]=='C') bx=i,by=j; 88 if(mp[i][j]=='F') ex=i,ey=j; 89 } 90 if(noans()) { 91 printf("no "); 92 return 0; 93 } 94 build(); 95 SPFA(); 96 printf("%d ",ans); 97 return 0; 98 }
T3:柱状图
法一:枚举最高点,三分最高点高度,并用树状数组计算花费,复杂度$O(nlogVlogn)$
具体说说怎么计算花费:
($a$[]表示初始值,$h$[]表示最终高度)
首先将$h_j=h_i-|i-j|$变为$$egin{cases} h_j=(h_i+i)-j &&i leq j \ h_j=(h_i-i)+j &&i>j end{cases}$$
因为最后要求的是$ sum_{j=1}^n |h_j-a_j|$
所以将上式代入,得:$$egin{cases} |h_j-a_j|=|(h_i+i)-(a_j+j)| &&i leq j \ |h_j-a_j|=|(h_i-i)-(a_j-j)| &&i>j end{cases}$$
观察上式,$i$为枚举出的值,$h_i$为三分出的值,所以问题转化为如何快速求出$|C-b_j|$ $(C=h_i-i , b_j=a_j pm j)$
$1.$可以使用平衡树:分别查询大于C的值的和、小于C的值的和、大于C的数的个数,小于C的数的个数
$2.$可以使用树状数组:观察到使用到的值只有$a_j-j$和$a_j+j$,只有$2*n$个,
可以离散后插入其排名对应的位置上,达到平衡树的效果。
法二:分析可得,当第$i$个数作为最高点时,如果我们将原数组减去一个先升后降的最小数列
问题就转化为如何将新序列变都为一个数,易知一定是将其都变为$max(1,中位数)$
如:对于数列$2,1,1,4,1$
当以第3个数为最高点时,可以减去$0,1,2,1,0$,变为$2,0,-1,3,1$,易知将其都变为1最优(中位数),花费为6
当以第4个数为最高点时,可以减去$0,1,2,3,2$,变为$2,0,-1,1,-1$,易知将其都变为1最优(保证原数均大于0),花费为6
于是有了一种对于以某点为最高点时$O(n)$的计算方法
如果枚举最高点位置,那么复杂度会变为$O(n^2)$,显然不行
但又无法优化枚举复杂度,所以使用模拟退火算法,用$O(n)$的复杂度来check
期望得分玄学,实际得分看rp,可AC
法一树状数组代码:

1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<vector> 7 #include<queue> 8 #define ll long long 9 using namespace std; 10 const int MAXN=200000; 11 const ll N=2000000000; 12 int n; 13 ll a[MAXN],tr[4][MAXN*2],bk[MAXN*2],btot,p[MAXN*2][2]; 14 ll ans=0x3f3f3f3f3f3f3f3f; 15 void add(int x,ll k,int id) { 16 for(;x<=btot+1;x+=x&-x) tr[id][x]+=k; 17 } 18 ll ask(int x,int id) { 19 ll ret=0; 20 for(;x;x-=x&-x) ret+=tr[id][x]; 21 return ret; 22 } 23 ll check(int x,ll hi) { 24 int pos1=upper_bound(bk+1,bk+btot+1,hi-x)-bk; 25 int pos2=upper_bound(bk+1,bk+btot+1,hi+x)-bk; 26 pos1=pos1?pos1-1:0; 27 pos2=pos2?pos2-1:0; 28 ll pres1=ask(pos1,0),sufs1=ask(btot+1,0)-ask(pos1,0); 29 ll pres2=ask(pos2,1),sufs2=ask(btot+1,1)-ask(pos2,1); 30 ll cnt1=ask(pos1,2),cnt2=ask(pos2,3); 31 return ((hi-x)*cnt1-pres1)+(sufs1-(hi-x)*(x-cnt1)) 32 +((hi+x)*cnt2-pres2)+(sufs2-(hi+x)*(n-x-cnt2)); 33 } 34 void solve(int x) { 35 ll l=max(x,n-x+1),r=N; 36 while(r-l>2) { 37 ll mid1=(l+r)>>1,mid2=mid1+1; 38 if(check(x,mid1)>check(x,mid2)) l=mid1; 39 else r=mid2; 40 } 41 ans=min(ans,check(x,l)); 42 ans=min(ans,check(x,r)); 43 ans=min(ans,check(x,l+1)); 44 } 45 int main() { 46 scanf("%d",&n); 47 for(int i=1;i<=n;i++) scanf("%lld",&a[i]); 48 for(int i=1;i<=n;i++) { 49 bk[++btot]=a[i]-i; 50 bk[++btot]=a[i]+i; 51 } 52 sort(bk+1,bk+btot+1); 53 btot=unique(bk+1,bk+btot+1)-bk-1; 54 for(int i=1;i<=n;i++) { 55 p[i][0]=lower_bound(bk+1,bk+btot+1,a[i]-i)-bk; 56 p[i][1]=lower_bound(bk+1,bk+btot+1,a[i]+i)-bk; 57 } 58 for(int i=1;i<=n;i++) add(p[i][1],a[i]+i,1),add(p[i][1],1,3); 59 for(int i=1;i<=n;i++) { 60 add(p[i][1],-a[i]-i,1); 61 add(p[i][0],a[i]-i,0); 62 add(p[i][1],-1,3); 63 add(p[i][0],1,2); 64 solve(i); 65 } 66 printf("%lld ",ans); 67 return 0; 68 }