jzojNOIP2014模拟 8.14总结

教主的花园 (Standard IO)

Time Limits: 1000 ms  Memory Limits: 65536KB     

Goto ProblemSet

Description

【问题背景】
LHX教主最近总困扰于前来膜拜他的人太多了,所以他给他的花园加上了一道屏障。 

【问题描述】
  可以把教主的花园附近区域抽像成一个正方形网格组成的网络,每个网格都对应了一个坐标(均为整数,有可能为负),若两个网格(x1, y1)(x2, y2)|x1 – x2| +|y1 – y2| = 1,则说这两个网格是相邻的,否则不是相邻的。
  教主在y = 0处整条直线上的网格设置了一道屏障,即所有坐标为(x, 0)的网格。当然,他还要解决他自己与内部人员的进出问题,这样教主设置了N个入口a1, a2, …,aN可供进出,即对于y = 0上的所有网格,只有 (a1, 0)(a2, 0) …… (aN, 0)可以通过,之外的所有纵坐标为0的网格均不能通过,而对于(x,y)y不为0的网格可以认为是随意通过的。
  现在教主想知道,给定M个点对(x1, y1)(x2, y2),并且这些点均不在屏障上,询问从一个点走到另一个点最短距离是多少,每次只能从一个格子走到相邻的格子。

Input

  输入的第1行为一个正整数N,为屏障上入口的个数。
  第2行有N个整数,a1, a2, …, aN,之间用空格隔开,为这N个入口的横坐标。
  第3行为一个正整数M,表示了M个询问。
  接下来M行,每行4个整数x1, y1, x2, y2,有y1y2均不等于0,表示了一个询问从(x1,y1)(x2, y2)的最短路。

Output

  输出共包含m行,第i行对于第i个询问输出从(x1, y1)(x2, y2)的最短路距离是多少。

Sample Input

2
2 -1
2
0 1 0 -1
1 1 2 2

Sample Output

4
2

Data Constraint
Hint

【数据规模】
  对于20%的数据,有nm≤10aixiyi绝对值不超过100
  对于40%的数据,有nm≤100aixiyi绝对值不超过1000
  对于60%的数据,有nm≤1000aixiyi绝对值不超过100000
  对于100%的数据,有nm≤100000aixiyi绝对值不超过100000000

对于这题,我爆零了……原因后话;

易证得:在x轴同侧,可以直接到达(即(y1<0&&y2<0)||(y1>0&&y2>0))【我将y1、y2打成x1、x2】,而异侧时,若在x轴上x1~x2(设x1<x2)间有入口,则可直接到达,而没有时,在[x1,x2]以外选取一个最优的门通过。

若是直接枚举门,n≤100000,会超时,于是乎可以排序后用二分求解。

注,虽然aixiyi绝对值不超过100000000但是想减后会爆intlongint),要用longlongint64

教主泡嫦娥 (Standard IO)

Time Limits: 1000 ms  Memory Limits: 65536KB     

Goto ProblemSet

Description

【问题背景】
20121221日下午31435秒,全世界各国的总统以及领导人都已经汇聚在中国的方舟上。
  但也有很多百姓平民想搭乘方舟,毕竟他们不想就这么离开世界,所以他们决定要么登上方舟,要么毁掉方舟。

LHX教主听说了这件事之后,果断扔掉了手中的船票。在地球即将毁灭的那一霎那,教主自制了一个小型火箭,奔向了月球……

  教主登上月球之后才发现,他的女朋友忘记带到月球了,为此他哭了一个月。
  但细心的教主立马想起了小学学过的一篇课文,叫做《嫦娥奔月》,于是教主决定,让嫦娥做自己的新任女友。

【题目描述】
  教主拿出他最新研制的LHX(Let's be Happy Xixi*^__^*)卫星定位系统,轻松地定位到了广寒宫的位置。
  见到嫦娥之后,教主用温柔而犀利的目光瞬间迷倒了嫦娥,但嫦娥也想考验一下教主。
  嫦娥对教主说:看到那边的环形山了么?你从上面那个环走一圈我就答应你~

  教主用LHX卫星定位系统查看了环形山的地形,环形山上一共有N个可以识别的落脚点,以顺时针1~N编号。每个落脚点都有一个海拔,相邻的落脚点海拔不同(第1个和第N个相邻)。
  教主可以选择从任意一个落脚点开始,顺时针或者逆时针走,每次走到一个相邻的落脚点,并且最后回到这个落脚点。
  教主在任意时刻,都会有上升下降两种状态的其中一种。

  当教主从第i个落脚点,走到第j个落脚点的时候(ij相邻)
j的海拔高于i的海拔:如果教主处于上升状态,教主需要耗费两段高度差的绝对值的体力;否则耗费高度差平方的体力。
j的海拔低于i的海拔:如果教主处于下降状态,教主需要耗费两段高度差的绝对值的体力;否则耗费高度差平方的体力。

  当然,教主可以在到达一个落脚点的时候,选择切换自己的状态(上升下降,下降上升),每次切换需要耗费M点的体力。在起点的时候,教主可以自行选择状态并且不算切换状态,也就是说刚开始教主可以选择任意状态并且不耗费体力。

  教主希望花费最少的体力,让嫦娥成为自己的女朋友。

Input

  输入的第一行为两个正整数NM,即落脚点的个数与切换状态所消耗的体力。
  接下来一行包含空格隔开的N个正整数,表示了每个落脚点的高度,题目保证了相邻落脚点高度不相同。

Output

  输出仅包含一个正整数,即教主走一圈所需消耗的最小体力值。
  注意:C++选手建议使用cout输出long long类型整数。

Sample Input

6 7
4 2 6 2 5 6

Sample Output

27

Data Constraint
Hint

【样例说明】
  从第3个落脚点开始以下降状态向前走,并在第4个落脚点时切换为上升状态。
  这样共耗费4 +(7)+3+1+2^2+2^2+4=27点体力。

【数据规模】
  对于10%的数据,N ≤ 10
  对于30%的数据,N ≤ 100a[i] ≤ 1000
  对于50%的数据,N ≤ 1000a[i] ≤ 100000
  对于100%的数据,N ≤ 10000a[i] ≤ 1000000M ≤ 1000000000

对于这题,我用了卡时……(直接暴力dp只有50points)

先讲暴力dp:用f【i】【j】表示到第i座山状态为j(0为上升,1为下降),这时可暴力枚举起点,按题目所说的去转移:

A[i]>a[i+1]:F[i+1][0]=min(f[i][1]+a[i]-a[i+1]+m,f[i][0]+sqr(a[i]-a[i+1]));

F[i+1][1]=min(f[i][1]+a[i]-a[i+1],f[i][0]+sqr(a[i]-a[i+1]+m));

A[i]<a[i+1]同理

时间复杂度为n^2,而且是longlong进行运算,会很慢,由于数据很水,可以用卡时,

保险起见也可用随机算法,随机起点,多次随机后形成拟正解。

 

测试时有人用倍增过了,在此说说,

可以明显得出,状态s[i,j]与状态s[j,k]可以分开处理,然后合起来就为状态s[I,k],由于n≤10000,直接枚举会超时+爆空间,因此可以预处理s[I,j]表示从i座山出发到包括它后第2^j座山的最优解,因为有上升下降之分,要再嵌一个b数组记录方向。复杂度为nlogn

 

此处讲讲o(n)的思路:

一种比较难以解释的:有人只从最高点和最低点分别做dp求最优解输出就过了……

附题解:正确的做法是使用三维动规:
f[i][j][k]表示第i个点的状态是j,并且有1号点状态为k推出的最小代价。

第二个点需要特殊处理:
if(tall[2]>tall[1])
{
f[2][0][0]=tall[2]-tall[1];
f[2][1][0]=tall[2]-tall[1]+M;
f[2][0][1]=sqr(tall[2]-tall[1])+M;
f[2][1][1]=sqr(tall[2]-tall[1]);



偶看

}
else
{
f[2][0][0]=sqr(tall[1]-tall[2]);
f[2][0][1]=tall[1]-tall[2]+M;
f[2][1][0]=sqr(tall[1]-tall[2])+M;
f[2][1][1]=tall[1]-tall[2];
}

转移就是(tall[i]>tall[i-1])
f[i][0][0]=min(f[i-1][0][0]+tall[i]-tall[i-1],f[i-1][1][0]+sqr(tall[i]-tall[i-1])+M);
f[i][1][0]=min(f[i-1][0][0]+tall[i]-tall[i-1]+M,f[i-1][1][0]+sqr(tall[i]-tall[i-1]));
f[i][0][1]=min(f[i-1][0][1]+tall[i]-tall[i-1],f[i-1][1][1]+sqr(tall[i]-tall[i-1])+M);
f[i][1][1]=min(f[i-1][0][1]+tall[i]-tall[i-1]+M,f[i-1][1][1]+sqr(tall[i]-tall[i-1]));

对于tall[i]<tall[i-1]这种情况以及逆时针的情况类似。
注意:顺时针推用的数组是f,逆时针推用的数组是g。
然后枚举每一个落脚点作为起始点,哪么从他开始绕一圈的代价就是
for(int i=2;i<=N;i++)
for(int j=0;j<=1;j++)
for(int k=0;k<=1;k++)
for(int m=0;m<=1;m++)
for(int n=0;n<=1;n++)
if(n==k) Min=min(Min,f[i][j][k]+g[N-i+2][m][n]+M);
    elseMin=min(Min,f[i][j][k]+g[N-i+2][m][n]);

这样时间复杂度是O(n)的

保镖排队 (Standard IO)

Time Limits: 1000 ms  Memory Limits: 65536KB     

Goto ProblemSet

Description

【问题背景】
  教主LHX作为知名人物,时刻会有恐怖分子威胁他的生命。于是教主雇佣了一些保镖来保障他的人生安全。

【题目描述】
  教主一共雇佣了N个保镖,编号为1~N。每个保镖虽然身手敏捷武功高强,但是他在其余N-1个保镖里,都会有一个上司,他会对他的上司言听计从。但一号保镖例外,他武功盖世,不惧怕其余任何保镖,所以他没有上司。
  教主LHX会对这N个保镖进行定期视察。每次视察的时候,首先会让所有保镖排队。
  对于每个保镖,在他心目中会对他的所有下属的武功实力排个队。
  现在教主要求排出来的队伍满足:互为上司-下属的两个保镖,上司在前,下属在后对于一个保镖的所有下属,武功实力较强的在前,较弱的在后。
  教主想知道,总的排队方法数除以10007的余数是多少。

Input

  输入的第一行为一个正整数T,表示了数据组数。
  对于每组数据:
  第一行为一个正整数N
  接下来N行,每行描述一个保镖。
  第i+1行,会有一个整数K,代表第i个保镖的下属个数,接下来K个数,代表第i个保镖的下属按照武功实力从高到低的编号。

Output

  输出包括C行,每行对于每组数据输出方案数mod 10007后的结果。

Sample Input

2
5
2 2 3
2 4 5
0
0
0
7
2 2 3
2 4 5
2 6 7
0
0
0
0

Sample Output

3
10

Data Constraint
Hint

【样例说明】
  对于第1组数据,有以下3种排列是合法的:
1 2 4 3 5
1 2 3 4 5
1 2 4 5 3
  同时满足了123之前且23之前,245之前且45之前

【数据规模】
  对于20%的数据,有N ≤ 9
  对于40%的数据,有对于所有K,有K ≤ 2
  对于60%的数据,有N ≤ 100
  对于100%的数据,有T ≤ 10N ≤ 1000K ≤ N

对于这题我们可以将其转化成一棵树,按题意所说的,就是将等级高的放在等级低的前面,这样我们可以先做等级低的,然后与稍高的合并(这样可以将较高的放在首位,确保较高的在较低的前面),再做等级高的:

对于第i个点,设排列数为f[i],其后继为a[j](前面的等级低于后面的),b【i】记录以节点i为根的树的节点数

则有:f[i]=f[i]*f[a[j]]*C(b[a[j]],y+b[a[j]]);(y记录已处理的后继树节点总数)

附代码:

#include<iostream>

#include<cstdio>

#include<algorithm>

intm,t,n,x,sum,y,ans,mod;

int g[10001],a[10001][2],b[10001],f[1001][1001];

voidin(int x,int y){

       a[++sum][0]=y;

       a[sum][1]=g[x];

       g[x]=sum;

}

voidinit(){

       sum=0;

       scanf("%d",&n);

       for (int i=1;i<=n;i++){

              scanf("%d",&x);

              b[i]=1;

              g[i]=0;

              for (int j=1;j<=x;j++){

                     scanf("%d",&y);

                     in(i,y);

              }

       }

}

intdfs(int x){

       int i=g[x];

       int s=1;

       int y=0;

       while (i!=0){

              s=(s*dfs(a[i][0]))%mod*f[y+b[a[i][0]]-1][b[a[i][0]]-1]%mod;

              y+=b[a[i][0]];

              b[x]+=b[a[i][0]];

              i=a[i][1];

       }

       return(s);

}

voidwork(){

       ans=dfs(1);

       printf("%d\n",ans);

}

intmain(){

       freopen("3.in","r",stdin);

       freopen("3.out","w",stdout);

       scanf("%d",&t);

       mod=10007;

       f[0][0]=1;

       for (int i=1;i<=1000;i++){

              f[i][0]=1;

              for (int j=1;j<=i;j++)

                     f[i][j]=(f[i-1][j]+f[i-1][j-1])%mod;

       }

       for (int i=1;i<=t;i++){

              init();

              work();

       }

       return0;

}

教主的别墅 (Standard IO)

Time Limits: 1000 ms  Memory Limits: 65536KB     

Goto ProblemSet

Description

【题目背景】
LHX教主身为宇宙第一富翁,拥有一栋富丽堂皇的别墅,由于别墅实在太大了,于是教主雇佣了许许多多的人来负责别墅的卫生工作,我们不妨称这些人为LHXee

【题目描述】
  教主一共雇佣了NLHXee,这些LHXee有男有女。
  教主的大别墅一共有M个房间,现在所有的LHXee在教主面前排成了一排。教主要把NLHXee分成恰好M个部分,每个部分在队列中都是连续的一段,然后分别去打扫M个房间。
  教主身为全世界知识最渊博的人,他当然知道男女搭配干活不累的道理,以及狼多羊少,羊多狼少的危害之大。所以教主希望一个分配方式,使得所有小组男女个数差的最大值最小。
  教主还希望你输出从左到右,每个组的人数。
  如果有多种人数组合都能达到最优值,教主希望你分别告诉他这些方案中字典序最小和最大的方案。换句话说,你需要找到两种方案,这两种方案满足所有组男女个数差最大值最小的前提下,第一种方案(字典序最小)要越靠前的组人数越少,也就是让第一个小组人尽量少,并且在第一个小组人尽量少的前提下,让第二个小组的人尽量少,依此类推;第二种方案(字典序最大)则要让越靠前的组人数越多。

Input

  输入的第1行为两个正整数NM,用空格分隔。
  第2行包含一个长度为N的串,仅由字符组成,第i个字符为0表示在这个位置上的LHXee为女生,若为1则为男生。

Output

  输出文件包含两行,每行M个正整数,正整数之间用空格隔开,行末无多余空格。这M个正整数从左到右描述了你所分的每个组的人数。
  第1行为字典序最小的方案,第2行为字典序最大的方案。

Sample Input

8 3
11001100

Sample Output

1 2 5
5 2 1

Data Constraint
Hint

【样例说明】
  字典序最小的方案按1, 10, 01100分组,每组男女个数差的最大值为1,为最小。
  字典序最大的方案按11001, 10, 0分组。

【数据规模】
  对于40%的数据,有N ≤ 100
  对于50%的数据,有N ≤ 1000
  对于65%的数据,有N ≤ 100000
  对于100%的数据,有N ≤ 5000000M ≤ NM ≤ 100000

【提示】
关于字典序:
比较S1[N]S2[N]的字典序大小,可以找到S1[N]S2[N]中第1个不相同数字S1[i]S2[i](即有对于所有1≤ki,都有S1[k]S2[k],但S1[i]≠S2[i])。如果S1[i]S2[i],那么说S1[N]字典序比S2[N]小,否则说S1[N]字典序比S2[N]大。

若我们对于a[i],若a[i]=”1”,则b[i]=1;若a[i]=”0”,则b[i]=-1。将b数组前缀和后,易证:最优方案下,最大男女人数差ans=(abs(a[n])-1)/m+1(即上取整),而对于a[n]=0时要特判:将b数组中0看做挡板,若分割的区间数不足m则ans=1,反之为0,

此时已知最大男女人数差,即可贪心出解:

这里讲第一问(第二问就是倒着做):设l为上次分组最后一个人的位置+1,即这次分组的开始,i为枚举到第i人,若有((abs(b[i]-b[l-1])>=ans)&&(abs(b[n]-b[i])>=ans))则可以分组i-l+1人,到第m-1组建立好时,剩余人数即第m组

附程序:

#include<iostream>

#include<cstdio>

#include<algorithm>

#include<cstring>

#include<cmath>

usingnamespace std;

int l,n,m,ans,x;

chars[5000200];

intc[100001],a[5000200];

intdid(int x,int y){

       if (x==0)return(0);

       return((x-1)/y+1);

}

intmain(){

       freopen("4.in","r",stdin);

       freopen("4.out","w",stdout);

       scanf("%d %d\n",&n,&m);

       scanf("%s",s);

       for (int i=0;i<=n-1;i++){

              a[i+1]=a[i];

              if (s[i]=='1')a[i+1]++;elsea[i+1]--;

       }

       if (a[n]==0){

              l=0;

              for (int i=1;i<=n;i++)if(a[i]==0)l++;

              if (l>=m-1)ans=0;else

              ans=1;

       }else

       ans=(abs(a[n])-1)/m+1;

       l=0;

       x=0;

       for (int i=1;i<=n;i++){

              if((abs(a[i]-a[l])<=ans)&&(did(abs(a[n]-a[i]),m-x-1)<=ans)){

                     printf("%d",i-l);

                     x++;

                     l=i;

                     if (x==m-1){

                            printf("%d\n",n-i);

                            break;

                     }

              }

       }

       l=n;

       x=0;

       for (int i=n;i>=1;i--)

              if((abs(a[l]-a[i-1])<=ans)&&(did(abs(a[i-1]),m-x-1)<=ans)){

                     x++;

                     c[m-x+1]=l-i+1;

                     l=i-1;

                     if (x==m-1){

                            c[1]=i-1;

                            break;

                     }    

              }

       for (int i=1;i<=m-1;i++)

              printf("%d ",c[i]);

       printf("%d\n",c[m]);

       return 0;

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值