教主的花园 (Standard IO)
Time Limits: 1000 ms Memory Limits: 65536KB
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,有y1与y2均不等于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%的数据,有n,m≤10,ai,xi,yi绝对值不超过100;
对于40%的数据,有n,m≤100,ai,xi,yi绝对值不超过1000;
对于60%的数据,有n,m≤1000,ai,xi,yi绝对值不超过100000;
对于100%的数据,有n,m≤100000,ai,xi,yi绝对值不超过100000000。
对于这题,我爆零了……原因后话;
易证得:在x轴同侧,可以直接到达(即(y1<0&&y2<0)||(y1>0&&y2>0))【我将y1、y2打成x1、x2】,而异侧时,若在x轴上x1~x2(设x1<x2)间有入口,则可直接到达,而没有时,在[x1,x2]以外选取一个最优的门通过。
若是直接枚举门,n≤100000,会超时,于是乎可以排序后用二分求解。
注,虽然ai,xi,yi绝对值不超过100000000,但是想减后会爆int(longint),要用longlong(int64)
教主泡嫦娥 (Standard IO)
Time Limits: 1000 ms Memory Limits: 65536KB
Description
【问题背景】
2012年12月21日下午3点14分35秒,全世界各国的总统以及领导人都已经汇聚在中国的方舟上。
但也有很多百姓平民想搭乘方舟,毕竟他们不想就这么离开世界,所以他们决定要么登上方舟,要么毁掉方舟。
LHX教主听说了这件事之后,果断扔掉了手中的船票。在地球即将毁灭的那一霎那,教主自制了一个小型火箭,奔向了月球……
教主登上月球之后才发现,他的女朋友忘记带到月球了,为此他哭了一个月。
但细心的教主立马想起了小学学过的一篇课文,叫做《嫦娥奔月》,于是教主决定,让嫦娥做自己的新任女友。
【题目描述】
教主拿出他最新研制的LHX(Let's be Happy Xixi*^__^*)卫星定位系统,轻松地定位到了广寒宫的位置。
见到嫦娥之后,教主用温柔而犀利的目光瞬间迷倒了嫦娥,但嫦娥也想考验一下教主。
嫦娥对教主说:“看到那边的环形山了么?你从上面那个环走一圈我就答应你~”
教主用LHX卫星定位系统查看了环形山的地形,环形山上一共有N个可以识别的落脚点,以顺时针1~N编号。每个落脚点都有一个海拔,相邻的落脚点海拔不同(第1个和第N个相邻)。
教主可以选择从任意一个落脚点开始,顺时针或者逆时针走,每次走到一个相邻的落脚点,并且最后回到这个落脚点。
教主在任意时刻,都会有“上升”、“下降”两种状态的其中一种。
当教主从第i个落脚点,走到第j个落脚点的时候(i和j相邻)
j的海拔高于i的海拔:如果教主处于上升状态,教主需要耗费两段高度差的绝对值的体力;否则耗费高度差平方的体力。
j的海拔低于i的海拔:如果教主处于下降状态,教主需要耗费两段高度差的绝对值的体力;否则耗费高度差平方的体力。
当然,教主可以在到达一个落脚点的时候,选择切换自己的状态(上升→下降,下降→上升),每次切换需要耗费M点的体力。在起点的时候,教主可以自行选择状态并且不算切换状态,也就是说刚开始教主可以选择任意状态并且不耗费体力。
教主希望花费最少的体力,让嫦娥成为自己的女朋友。
Input
输入的第一行为两个正整数N与M,即落脚点的个数与切换状态所消耗的体力。
接下来一行包含空格隔开的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 ≤ 100,a[i] ≤ 1000;
对于50%的数据,N ≤ 1000,a[i] ≤ 100000;
对于100%的数据,N ≤ 10000,a[i] ≤ 1000000,M ≤ 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
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
同时满足了1在2与3之前且2在3之前,2在4与5之前且4在5之前
【数据规模】
对于20%的数据,有N ≤ 9;
对于40%的数据,有对于所有K,有K ≤ 2;
对于60%的数据,有N ≤ 100;
对于100%的数据,有T ≤ 10,N ≤ 1000,K ≤ 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
Description
【题目背景】
LHX教主身为宇宙第一富翁,拥有一栋富丽堂皇的别墅,由于别墅实在太大了,于是教主雇佣了许许多多的人来负责别墅的卫生工作,我们不妨称这些人为LHXee。
【题目描述】
教主一共雇佣了N个LHXee,这些LHXee有男有女。
教主的大别墅一共有M个房间,现在所有的LHXee在教主面前排成了一排。教主要把N个LHXee分成恰好M个部分,每个部分在队列中都是连续的一段,然后分别去打扫M个房间。
教主身为全世界知识最渊博的人,他当然知道男女搭配干活不累的道理,以及狼多羊少,羊多狼少的危害之大。所以教主希望一个分配方式,使得所有小组男女个数差的最大值最小。
教主还希望你输出从左到右,每个组的人数。
如果有多种人数组合都能达到最优值,教主希望你分别告诉他这些方案中字典序最小和最大的方案。换句话说,你需要找到两种方案,这两种方案满足所有组男女个数差最大值最小的前提下,第一种方案(字典序最小)要越靠前的组人数越少,也就是让第一个小组人尽量少,并且在第一个小组人尽量少的前提下,让第二个小组的人尽量少,依此类推;第二种方案(字典序最大)则要让越靠前的组人数越多。
Input
输入的第1行为两个正整数N与M,用空格分隔。
第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 ≤ 5000000,M ≤ N且M ≤ 100000。
【提示】
关于字典序:
比较S1[N]与S2[N]的字典序大小,可以找到S1[N]与S2[N]中第1个不相同数字S1[i]与S2[i](即有对于所有1≤k<i,都有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;
}