08-07 NOIP模拟测试14

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小的就直接能查到$ \sum\limits 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\n",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\n",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 }
vector

 

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\n",dis[i]);
188     if(!vis[id[ed_x][ed_y]]) puts("no");
189     else printf("%d\n",dis[id[ed_x][ed_y]]);
190     return 0;
191 }
View Code

 

C. 柱状图

yy了下觉得高度可以三分,然后脑抽觉得位置也可以。然后就码了个三分套三分套O(n)验证。

后来又码了个拍,发现拍个几组就会出错,且答案相差不多。

再想便发现是位置不满足单峰函数,脑补下w。

然后对高度三分跑拍,验证正确性get。

如果不加外层三分,复杂度就会到达。O(n2log1.5n)

由于它的正确率较高,我打算直接测试点分治让它跑大点。

于是拿到了80分,其中应该有10是TLE。

 

skyh 也对位置进行了三分,但他通过预处理

 

posted @ 2019-08-08 14:32  hzoi_yzh  阅读(182)  评论(1编辑  收藏  举报