noip模拟测试14


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\n",ans);
39     return 0;
40 }
t1 Code

 


 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\n");
92         return 0;
93     }
94     build();
95     SPFA();
96     printf("%d\n",ans);
97     return 0;
98 }
t2 Code

 


 

T3:柱状图

  法一:枚举最高点,三分最高点高度,并用树状数组计算花费,复杂度$O(nlogVlogn)$

     具体说说怎么计算花费:

      ($a$[]表示初始值,$h$[]表示最终高度)

      首先将$h_j=h_i-|i-j|$变为$$\begin{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|$

      所以将上式代入,得:$$\begin{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\n",ans);
67     return 0;
68 }
t3 Code

 

posted @ 2019-08-08 14:33  G_keng  阅读(174)  评论(0编辑  收藏  举报