A. 旋转子段
考场上先想到了fhq的区间翻转,然而并没用从fhq的操作中找到什么解题的思路,又这道题数据范围一看就不能真去翻转,然后很快就算把这个弃掉了。
之后又想到一些神奇的东西,比如说我把i与a[i]做差得到下标是i的新数组,它的含义是我这个元素离我要去的固定点差多少,或者相反的,表示我要去哪。
然后就发现最优情况是新数组的最长的满足旋转性质的回文字串,性质即是 -2 0 2 这种特定的串,然后就想先预处理出这些串,为了查询,把它们hash掉放到哈希表里。
过了几秒就发现自己又**忘了题意开始自己瞎YY了,我找完全回文串有个毛用啊#$%#%@。
然后想清清脑子先放一放,打了个$Theta (n^3)$ 暴力走了。
正解:
假设我们有一段区间[i,j],如果a[i]!=j&&a[j]!=i,i j都不能通过这次旋转变成固定点,那我为什么不旋转[i+1,j-1]?
我们一直在上面的判断不成立的时候去掉两端,最终会得到a[i]==j||a[j]==i,相当于是这种贡献的最简表达形式。
然后我们就知道了对于一个答案的贡献区间一共有n个,分别为$ [min(i,a[i]),max(i,a[i])] $。
得到了这个性质,我们考虑计算。
把每个贡献拆成三段,[i,l-1],[l,r],[r+1,n]。对于两侧的静态答案可以用前缀和$ Theta (N) $ 处理,直接$ Theta (1) $查
对于[l,r],我们发现对于所有区间内的固定点j满足 a[j]+j==a[i]+i,含义上理解就是a[i]与i a[j]与j关于同一条轴对称,所以翻转后a[j]到了j就成了固定点。
所以可以建桶,把区间内的a[j]+j装进桶里,然后用a[i]+i查询即可。
然而暴力装桶复杂度是n2的。有以下算法优化:
卡常可A算法一:莫队
套路离线排序直接瞎搞即可。
想卡过需要以下:奇偶排序,预处理前缀和,预处理每个询问左端点所在块(很重要)。fh。
算法二:这是正解
对于轴心分类建vector存轴心为这个点的区间,每个vector按区间长度从大到小排序。
这样我们对于一个区间,直接查下vec[i].size()-j就能得到[l,r]的'桶'。
正确性:其实上面有说,因为a[j]+j和a[i]+i关于同一个轴对称,我查这个轴且区间长度比i小的就直接能查到$ sumlimits a[j]+j $。
而a[j]+j其实也是我们要的一个贡献区间,所以我们一直操作下去即可。
由于只有n个区间,这个看似O(n2)的算法其实是O(n)的
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 #define reg register 5 #define F(i,a,b) for(register int (i)=(a);(i)<=(b);++(i)) 6 const int L=1<<20|1; 7 char buffer[L],*S,*T; 8 #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++) 9 using namespace std; 10 int read(); 11 const int N=500005; 12 int n,t; 13 int a[N]; 14 int sum[N]; 15 int bk[N<<1]; 16 struct Q{ 17 int id,l,r,bl; 18 friend bool operator<(const Q &a,const Q &b) 19 { 20 return a.bl==b.bl?((a.bl&1)?a.r<b.r:a.r>b.r):a.bl<b.bl; 21 } 22 }q[N]; 23 int main() 24 { 25 n=read(); 26 t=pow(n,0.53); 27 F(i,1,n) 28 { 29 a[i]=read(); 30 sum[i]=sum[i-1]+(a[i]==i); 31 } 32 F(i,1,n) 33 { 34 q[i].l=min(i,a[i]); 35 q[i].r=max(i,a[i]); 36 q[i].bl=q[i].l/t; 37 q[i].id=i; 38 } 39 sort(q+1,q+n+1); 40 reg int l=1,r=0,ans=1; 41 F(i,1,n) 42 { 43 if(sum[q[i].l-1]+sum[n]-sum[q[i].r]+(q[i].r-q[i].l+1)<=ans) continue; 44 while(l<q[i].l) 45 { 46 --bk[a[l]+l]; 47 ++l; 48 } 49 while(l>q[i].l) 50 { 51 --l; 52 ++bk[a[l]+l]; 53 } 54 while(r<q[i].r) 55 { 56 ++r; 57 ++bk[a[r]+r]; 58 } 59 while(r>q[i].r) 60 { 61 --bk[a[r]+r]; 62 --r; 63 } 64 ans=max(ans,sum[q[i].l-1]+sum[n]-sum[q[i].r]+bk[a[q[i].id]+q[i].id]); 65 if(ans==n) break; 66 } 67 printf("%d ",ans); 68 return 0; 69 } 70 int read() 71 { 72 reg int x=0; 73 reg char tc=getchar(); 74 while(tc<'0'||tc>'9') tc=getchar(); 75 while(tc>='0'&&tc<='9') x=x*10+tc-48,tc=getchar(); 76 return x; 77 }
1 #include<cstdio> 2 #include<cmath> 3 #include<vector> 4 #include<algorithm> 5 #define reg register 6 #define F(i,a,b) for(register int (i)=(a);(i)<=(b);++(i)) 7 //const int L=1<<20|1; 8 //char buffer[L],*S,*T; 9 //#define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++) 10 using namespace std; 11 int read(); 12 const int N=500005; 13 int n,t; 14 int a[N]; 15 int sum[N]; 16 vector<int> q[N<<1]; 17 bool cmp(int a,int b) 18 { 19 return a>b; 20 } 21 int main() 22 { 23 n=read(); 24 F(i,1,n) 25 { 26 a[i]=read(); 27 sum[i]=sum[i-1]+(a[i]==i); 28 } 29 int l,r; 30 F(i,1,n) 31 { 32 l=min(i,a[i]); 33 r=max(i,a[i]); 34 q[l+r].push_back(r-l+1); 35 } 36 int lim=n<<1; 37 F(i,2,lim) 38 { 39 sort(q[i].begin(),q[i].end(),cmp); 40 } 41 int ans=1; 42 F(i,2,lim) 43 { 44 for(reg int j=0;j<q[i].size();++j) 45 { 46 r=(q[i][j]+i-1)>>1; 47 l=i-r; 48 ans=max(ans,sum[l-1]+sum[n]-sum[r]+(int)q[i].size()-j); 49 } 50 } 51 printf("%d ",ans); 52 return 0; 53 } 54 int read() 55 { 56 reg int x=0; 57 reg char tc=getchar(); 58 while(tc<'0'||tc>'9') tc=getchar(); 59 while(tc>='0'&&tc<='9') x=x*10+tc-48,tc=getchar(); 60 return x; 61 }
B. 走格子
鲜鮕亿下...
这题一眼看上去就像是个搜索加剪枝,然而我就真的这么以为了。
其实从优化搜索的角度,使用传送门,就相当与花费里我最近的墙的距离+1,而到达以我为中心向四个方向正对的墙前一个格子。
然后就能发现这个搜索已经没有必要了,直接建好图跑dij就好了。
建图:
1.我向四个方向的非墙格子,权值1
2.我向四个方向正对的墙前一个格子,权值离我最近的墙+1
然后这题没了。
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<cmath> 5 #include<queue> 6 #include<algorithm> 7 #define ll long long 8 #define reg register 9 #define F(i,a,b) for(register int (i)=(a);(i)<=(b);++(i)) 10 using namespace std; 11 void find_wall(); 12 const int N=505; 13 int n,m; 14 int z[N][N],id[N][N],cnt; 15 int tx[5]={0,1,0,-1},ty[5]={1,0,-1,0}; 16 int near[N][N]; 17 int temp[N]; 18 int dis[N*N]; 19 bool vis[N*N]; 20 int st_x,st_y,ed_x,ed_y; 21 struct node{ 22 int x,y,step; 23 }; 24 struct DJ{ 25 int id,dis; 26 friend bool operator<(const DJ &a,const DJ &b) 27 { 28 return a.dis>b.dis; 29 } 30 }; 31 priority_queue<DJ> q; 32 queue<node> bq; 33 int fir[N*N],o=1; 34 struct R{ 35 int u,v,w,next; 36 }r[19260817]; 37 void add(int u,int v,int w) 38 { 39 if(u==v) return; 40 r[++o].u=u; 41 r[o].v=v; 42 r[o].w=w; 43 r[o].next=fir[u]; 44 fir[u]=o; 45 } 46 void dj() 47 { 48 dis[id[st_x][st_y]]=0; 49 q.push((DJ){id[st_x][st_y],0}); 50 int u; 51 while(!q.empty()) 52 { 53 u=q.top().id; 54 q.pop(); 55 if(vis[u]) continue; 56 vis[u]=1; 57 for(reg int i=fir[u];i;i=r[i].next) 58 { 59 int v=r[i].v; 60 if(dis[v]>dis[u]+r[i].w) 61 { 62 dis[v]=dis[u]+r[i].w; 63 q.push((DJ){v,dis[v]}); 64 } 65 } 66 } 67 } 68 void bfs() 69 { 70 reg int x,y,step; 71 while(!bq.empty()) 72 { 73 x=bq.front().x; 74 y=bq.front().y; 75 step=bq.front().step; 76 bq.pop(); 77 if(near[x][y]!=0x3f3f3f3f) continue; 78 if(z[x][y]) 79 { 80 near[x][y]=step; 81 } 82 F(i,0,3) 83 { 84 int nx,ny; 85 nx=x+tx[i]; 86 ny=y+ty[i]; 87 if(z[nx][ny]==0) continue; 88 bq.push((node){nx,ny,step+1}); 89 } 90 } 91 } 92 void build() 93 { 94 F(x,1,n) 95 { 96 F(y,1,m) 97 { 98 if(z[x][y]==0||z[x][y]==3) continue; 99 F(i,0,3) 100 { 101 int nx,ny; 102 nx=x+tx[i]; 103 ny=y+ty[i]; 104 if(z[nx][ny]) 105 { 106 add(id[x][y],id[nx][ny],1); 107 } 108 } 109 } 110 } 111 find_wall(); 112 } 113 void find_wall() 114 { 115 int wall=1; 116 F(i,1,n) 117 { 118 F(j,1,m) 119 { 120 if(z[i][j]==0) wall=j; 121 else add(id[i][j],id[i][wall+1],near[i][j]); //pan 自环 122 } 123 for(reg int j=m;j>=1;--j) 124 { 125 if(z[i][j]==0) wall=j; 126 else add(id[i][j],id[i][wall-1],near[i][j]); 127 } 128 } 129 F(j,1,m) 130 { 131 F(i,1,n) 132 { 133 if(z[i][j]==0) wall=i; 134 else add(id[i][j],id[wall+1][j],near[i][j]); 135 } 136 for(reg int i=n;i>=1;--i) 137 { 138 if(z[i][j]==0) wall=i; 139 else add(id[i][j],id[wall-1][j],near[i][j]); 140 } 141 } 142 } 143 int main() 144 { 145 memset(dis,0x3f,sizeof(dis)); 146 memset(near,0x3f,sizeof(near)); 147 scanf("%d %d",&n,&m); 148 F(i,1,n) 149 { 150 F(j,1,m) 151 { 152 char tc=getchar(); 153 while(tc!='#'&&tc!='.'&&tc!='C'&&tc!='F') tc=getchar(); 154 if(tc=='#') 155 { 156 z[i][j]=0; 157 bq.push((node){i,j,0}); 158 } 159 else if(tc=='.') z[i][j]=1; 160 else if(tc=='C') 161 { 162 st_x=i; 163 st_y=j; 164 z[i][j]=2; 165 } 166 else if(tc=='F') 167 { 168 ed_x=i; 169 ed_y=j; 170 z[i][j]=3; 171 } 172 if(z[i][j]) id[i][j]=++cnt; 173 } 174 } 175 bfs(); 176 build(); 177 /* F(i,1,n) 178 { 179 F(j,1,m) 180 { 181 if(near[i][j]==0x3f3f3f3f) putchar('-'); 182 else printf("%d",near[i][j]); 183 } 184 puts(""); 185 }*/ 186 dj(); 187 // F(i,1,cnt) printf("%d ",dis[i]); 188 if(!vis[id[ed_x][ed_y]]) puts("no"); 189 else printf("%d ",dis[id[ed_x][ed_y]]); 190 return 0; 191 }
C. 柱状图
yy了下觉得高度可以三分,然后脑抽觉得位置也可以。然后就码了个三分套三分套O(n)验证。
后来又码了个拍,发现拍个几组就会出错,且答案相差不多。
再想便发现是位置不满足单峰函数,脑补下w。
然后对高度三分跑拍,验证正确性get。
如果不加外层三分,复杂度就会到达。O(n2log1.5n)
由于它的正确率较高,我打算直接测试点分治让它跑大点。
于是拿到了80分,其中应该有10是TLE。
skyh 也对位置进行了三分,但他通过预处理