福州大学第十三届程序设计竞赛_重现解题报告

来源:互联网 发布:招标文件制作软件 编辑:程序博客网 时间:2024/06/14 02:00

此文章可以使用目录功能哟↑(点击上方[+])

都快要退役了,但是感觉自己还是实力不够,智商捉急...

链接→福州大学第十三届程序设计竞赛_重现

 Problem A Calculus Midterm

Accept: 0    Submit: 0
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

Yellowstar刚考完微积分期中考,微积分期中考卷一共三个部分,8道选择题每题3分,8道填空题,每题2分,10题答题每题6分,总分100分。Yellowstar考完期中考后已经失去了计算能力,他只记得每部分对的题数,现在想请你帮忙计算一下他的得分,能否通过考试。

 Input

多组数据,处理到EOF。

每组数据输入3个整数 x,y,z(0<=x<=8,0<=y<=8,0<=z<=10)分别表示选择题、填空题和简答题正确的题数。

 Output

每组数据需要输出两行,如果他最少的得分大于等于60 ,第一行输出“I passed the exam.”(双引号不需要输出),第二行输出他的得分;如果他最低得分小于60,第一行输出“Exam was too hard.”(双引号不需要输出),第二行输出他的得分。

 Sample Input

8 8 10
0 0 0

 Sample Output

I passed the exam.
100
Exam was too hard.
0

 Problem Idea

解题思路:水题,单纯判断3*x+2*y+6*z是否大于等于60

题目链接→Problem 2229 Calculus Midterm

 Source Code

/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 100005;const int M = 40;const int inf = 100000000;const int mod = 2009;int main(){    int x,y,z,sum;    while(~scanf("%d%d%d",&x,&y,&z))    {    sum=x*3+y*2+z*6;    if(sum>=60)    puts("I passed the exam.");   else   puts("Exam was too hard.");printf("%d\n",sum);    }    return 0;}


 Problem B 翻翻棋

Accept: 0    Submit: 0
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

象棋翻翻棋(暗棋)中双方在4*8的格子中交战,有时候最后会只剩下帅和将。根据暗棋的规则,棋子只能上下左右移动,且相同的级别下,主动移动到地方棋子方将吃掉对方的棋子。将和帅为同一级别。然而胜负在只剩下帅和将的时候已定。

 Input

第一行T,表示T组数据。

每组数据共有四行字符串,每行字符串共八个字符

’#’表示空格

’*’表示红方帅

’.’表示黑方将

此时红方先走

每组输入之间没有空行。

 Output

每组数据输出一行。若为红方赢输出Red win,否则输出 Black win

 Sample Input

1
######.#
#####*##
########
########

 Sample Output

Black win

 Problem Idea

解题思路:水题,判断曼哈顿距离(|x1-x2|+|y1-y2|)的奇偶性,若为奇数,先手必胜;若为偶数,先手必败

题目链接→Problem 2230 翻翻棋

 Source Code

/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 100005;const int M = 40;const int inf = 100000000;const int mod = 2009;char s[4][15];int main(){    int t,i,j,x1,y1,x2,y2,d;    scanf("%d",&t);    while(t--)    {    for(i=0;i<4;i++)    scanf("%s",s[i]);   for(i=0;i<4;i++)   for(j=0;j<8;j++)   if(s[i][j]=='*')   x1=i,y1=j;else if(s[i][j]=='.')x2=i,y2=j;d=abs(x2-x1)+abs(y2-y1);if(d%2)puts("Red win");elseputs("Black win");    }    return 0;}

 Problem C 平行四边形数

Accept: 0    Submit: 0
Time Limit: 2000 mSec    Memory Limit : 32768 KB

 Problem Description

在一个平面内给定n个点,任意三个点不在同一条直线上,用这些点可以构成多少个平行四边形?一个点可以同时属于多个平行四边形。

 Input

多组数据(<=10),处理到EOF。

每组数据第一行一个整数n(4<=n<=500)。接下来n行每行两个整数xi,yi(0<=xi,yi<=1e9),表示每个点的坐标。

 Output

每组数据输出一个整数,表示用这些点能构成多少个平行四边形。

 Sample Input

4
0 1
1 0
1 1
2 0

 Sample Output

1

 Problem Idea

解题思路:给你n个点,求能构成多少个平行四边形。首先,根据平行四边形的一个定义:对角线平分的四边形为平行四边形

故而,我们可以求出任意两点的中点,由于题目说任意三个点都不在同一直线上,所以排除了以下情况:


那么,接下来要做的就是计算k对有共同中点的点集(每个点集包含两个点)能组成多少个平行四边形,即k(k-1)/2

题目链接→Problem 2231 平行四边形数

 Source Code

/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 505;const int M = 40;const int inf = 100000000;const int mod = 2009;struct node{int x,y;}s[N],d[N*N];bool cmp(node x,node y){if(x.x!=y.x)return x.x<y.x;return x.y<y.y;}int main(){    int n,i,j,p,c;    while(~scanf("%d",&n))    {    p=c=0;    for(i=0;i<n;i++)    scanf("%d%d",&s[i].x,&s[i].y);    for(i=0;i<n;i++)    for(j=i+1;j<n;j++)   {   d[p].x=s[i].x+s[j].x;   d[p].y=s[i].y+s[j].y;   p++;  }  sort(d,d+p,cmp);  for(i=0,j=1;i<p&&j<p;i=j,j++)  {  while(d[j].x==d[i].x&&d[j].y==d[i].y)  j++;  c+=(j-i-1)*(j-i)/2;}printf("%d\n",c);    }    return 0;}

 Problem D 炉石传说

Accept: 0    Submit: 0
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

GG学长虽然并不打炉石传说,但是由于题面需要他便学会了打炉石传说。但是传统的炉石传说对于刚入门的GG学长来说有点复杂,所以他决定自己开发一个简化版的炉石传说。

在简化版的炉石传说中:

每个随从只有生命值和攻击力,并且在你的回合下,你的每只随从在本回合下只能选择一个敌方随从进行攻击。当两个随从a,b交战时,a的生命值将减去b的攻击力,b的生命值将减去a的攻击力,(两个伤害没有先后顺序,同时结算)。如果a或b的生命值不大于0,该随从将死亡。

某一次对局中,GG学长和对手场面上均有n个随从,并且是GG学长的回合。由于GG学长是个固执的boy,他一定要在本回合杀死对方所有随从,并且保证自己的随从全部存活。他想知道能否做到。

 Input

第一行为T,表示有T组数据。T<=100。

每组数据第一行为n,表示随从数量(1 <= n <= 100)

接下来一行2 * n个数字a1, b1, a2, b2, ... , an, bn (1 <= ai, bi <= 100)

表示GG学长的n个随从,ai表示随从生命,bi表示随从攻击力

接下来一行2 * n个数字c1, d1, c2, d2, ... , cn, dn (1 <= ci, di <= 100)

表示对手的n个随从,ci表示随从生命,di表示随从攻击力。

 Output

每组数据,根据GG是否能完成他的目标,输出一行”Yes”或”No”。

 Sample Input

2
3
4 4 5 5 6 6
1 1 2 2 3 3
3
4 4 5 5 6 6
1 4 2 4 3 4

 Sample Output

Yes
No

 Problem Idea

解题思路:因为GG学长和对手均有n个随从,GG学长先手,且每个随从只能攻击对方的一个随从,很明显是二分匹配问题,而解决二分匹配的问题,常常可以转化为网络流来做,那简单了,只要我们把图给建出来,剩下的就交给网络流模板了

首先,我们要添加一个源点1,和汇点2*n+2,源点1到点2~n+1都有一条流量为1的路,而点n+2~2*n+1到汇点2*n+2也都有一条流量为1的路

其次考虑点2~n+1与点n+2~2*n+1之间的情况,很明显,只有在GG学长的一个随从X攻击大于等于对方随从Y的血量,且GG学长的该随从X血量大于对方随从Y的攻击,这种情况才有一条从点X到点Y的流量为1的路

最后,我们只要判断该图的最大流是否为n即可

题目链接→Problem 2232 炉石传说

 Source Code

/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")  #include<stdio.h>  #include<string.h>  #include<stdlib.h>  #include<queue>  #include<stack>  #include<math.h>  #include<vector>  #include<map>  #include<set>  #include<cmath>  #include<string>  #include<algorithm>  #include<iostream>  #define exp 1e-10  using namespace std;  const int N = 505;  const int M = 1005;  const int inf = 1000000007;  const int mod = 2009;  #include<iostream>  #include<cstdio>  #include<cstring>  using namespace std;  int Map[N][N];//存图  int pre[N];//记录当前点的前驱  int level[N];//记录距离标号  int gap[N];//gap常数优化  int NV,NE;  //入口参数vs源点,vt汇点  int SAP(int vs,int vt,int n)  {      memset(pre,-1,sizeof(pre));      memset(level,0,sizeof(level));      memset(gap,0,sizeof(gap));      gap[0]=n;      int v,u=pre[vs]=vs,maxflow=0,aug=inf;      while(level[vs]<n)      {          //寻找可行弧          for(v=1;v<=n;v++)              if(Map[u][v]>0&&level[u]==level[v]+1)                  break;          if(v<=n)          {              pre[v]=u;              u=v;              if(v==vt)              {                  aug=inf;                  //寻找当前找到的一条路径上的最大流                  for(int i=v;i!=vs;i=pre[i])                      if(aug>Map[pre[i]][i])                          aug=Map[pre[i]][i];                  maxflow+=aug;                  //更新残留网络                  for(int i=v;i!=vs;i=pre[i])                  {                      Map[pre[i]][i]-=aug;                      Map[i][pre[i]]+=aug;                  }                  u=vs;//从源点开始继续搜              }          }          else          {              //找不到可行弧              int minlevel=n;              //寻找与当前点相连接的点中最小的距离标号              for(v=1;v<=n;v++)                  if(Map[u][v]>0&&minlevel>level[v])                      minlevel=level[v];              gap[level[u]]--;//(更新gap数组)当前标号的数目减1;              if(gap[level[u]]==0)                  break;//出现断层              level[u]=minlevel+1;              gap[level[u]]++;              u=pre[u];          }      }      return maxflow;  } int a[N],b[N],c[N],d[N]; int main()  {      int n,m,u,v,cap;      int t,i,j;    scanf("%d",&t);    while(t--)    {    memset(Map,0,sizeof(Map));    scanf("%d",&n);    for(i=1;i<=n;i++)    scanf("%d%d",&a[i],&b[i]);   for(i=1;i<=n;i++)    scanf("%d%d",&c[i],&d[i]);   for(i=1;i<=n;i++)   {   Map[1][i+1]=1;for(j=1;j<=n;j++)   {if(a[i]>d[j]&&b[i]>=c[j])   Map[i+1][j+n+1]=1;Map[j+n+1][2*n+2]=1;     }      }     if(SAP(1,n*2+2,n*2+2)==n)    puts("Yes");elseputs("No");    }    return 0;  }  


 Problem E ~APTX4869

Accept: 0    Submit: 0
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

为了帮助柯南回到一米七四,阿笠博士夜以继日地研究APTX4869的解药。他得出了如下结果:

1.解药由n种原料构成;

2.对于两种不同的的原料a,b,它们之间有个影响值f(a,b);

3.需要把原料分成两个部分X,Y,每部分中至少有一种原料;

4.解药的效果由分别属于X,Y的原料之间,最小的影响值决定,即

效果=min{f(a,b)|a∈X,b∈Y)}

博士需要你帮忙求出:在所有的方案中,最大的效果值可以是多少?

 Input

多组数据(<=10),处理到EOF。

每组数据输入第一行为一个正整数n。

接下去是一个n行n列的整数矩阵,同一行的数以空格隔开。矩阵第i行j列表示第i种和第j种材料的影响值f(i,j)。给出的矩阵是对称的,即f(i,j)=f(j,i)。当i=j时,f(i,i)没有意义,矩阵该处的值为-1。

2<=n<=800。当i!=j时,0<=f(i,j)<=1000000;当i=j时,f(i,j)=-1。

 Output

每组数据输出一行,表示最大可能的效果值。

 Sample Input

3
-1 100 300
100 -1 200
300 200 -1

 Sample Output

200

 Problem Idea

解题思路:为了效果值能够尽可能大,我们应该避免选到小的f(i,j)

那换种思路,只要不存在相对较小的f(i,j),那我们就可以避免取到小的f(i,j)

那如何使相对较小的f(i,j)不存在呢?很简单,只要第i种材料与第j种材料属于同一类,那么第i种材料与第j种材料之间就不存在影响值

而我们需要做的就是,首先二分答案,然后将所有小于二分值的影响值f(i,j)中的两种材料划分到同一类,最后判断有几个连通块就行

判连通块的方法有很多种,本人采用并查集来实现

题目链接→Problem 2233 ~APTX4869

 Source Code

/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 805;const int M = 40;const int inf = 100000000;const int mod = 2009;int n,s[N][N],c[N],b[N*N/2],p;bool v[N];int fun(int x)  {      if(c[x]!=x)          c[x]=fun(c[x]);      return c[x];  } void init(){for(int i=1;i<=n;i++)c[i]=i;memset(v,false,sizeof(v));}bool judge(int x){int i,j,k,t=0;init();for(i=1;i<=n;i++)for(j=i+1;j<=n;j++)if(s[i][j]<x)c[fun(i)]=fun(j); for(i=1;i<=n;i++){k=fun(i);if(!v[k]){t++;v[k]=true;}}if(t>1)return true;return false;}void Binary()//二分答案 {    int l=0,r=p-1,ans=0;    while(l<r)    {        int mid=(l+r)>>1;        if(judge(b[mid])){l=mid+1;ans=max(ans,b[mid]);}        else r=mid;    }    if(l==r&&judge(b[l]))    ans=max(ans,b[l]);    printf("%d\n",ans);}int main(){    int i,j;    while(~scanf("%d",&n))    {    p=0;    for(i=1;i<=n;i++)    for(j=1;j<=n;j++)   {   scanf("%d",&s[i][j]);   if(j>i)   b[p++]=s[i][j];   }   sort(b,b+p);   p=unique(b,b+p)-b;Binary();    }    return 0;}/*3-1 100 300100 -1 200300 200 -14-1 200 200 100200 -1 200 100200 200 -1 200100 100 200 -14-1 200 100 100200 -1 200 100100 200 -1 200100 100 200 -1*/

 Problem F 牧场物语

Accept: 0    Submit: 0
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

小茗同学正在玩牧场物语。该游戏的地图可看成一个边长为n的正方形。

小茗同学突然心血来潮要去砍树,然而,斧头在小茗的右下方。

小茗是个讲究效率的人,所以他会以最短路程走到右下角,然后再返回到左上角。并且在路上都会捡到/踩到一些物品,比如说花朵,钱和大便等。

物品只能被取最多一次。位于某个格子时,如果格子上还有物品,就一定要取走。起点和终点上也可能有物品。

每种物品我们将为其定义一个价值,当然往返之后我们取得的物品的价值和越大越好。但是小茗同学正在认真地玩游戏,请你计算出最大的价值和。

 Input

多组数据(<=10),处理到EOF。

第一行输入正整数N(N≤100),表示正方形的大小。

接下来共N行,每行N个整数Ai,j(|Ai,j|≤10^9),表示相应对应位置上物品的价值。值为0表示没有物品。

 Output

每组数据输出一个整数,表示最大价值和。

 Sample Input

2
11 14
16 12

 Sample Output

53

 Problem Idea

解题思路:小茗从左上角走到右下角再走回左上角,这个问题可以转换为两个人同时从左上角到右下角,使得路上取得最大权值

显然,此题就变成了一道DP题

令ans[k][(x1,y1)][(x2,y2)]表示1号小茗第k步走到点(x1,y1),同时2号小茗第k步走到(x2,y2),两人取得的权值之和

很容易的,我们可以得到转移方程

ans[k][(x1,y1)][(x2,y2)]=max{

ans[k-1][(x1-1,y1)][(x2-1,y2)],

ans[k-1][(x1,y1-1)][(x2,y2-1)],

ans[k-1][(x1-1,y1)][(x2,y2-1)],

ans[k-1][(x1,y1-1)][(x2-1,y2)]

}

另外,题目说小茗会以最短路程走到右下角,那么,说明小茗从起点到终点需要k=n+n-2步

而k与坐标点(x,y)的关系为k+2=x+y,因为0步的时候小茗位于(0,0)

那么我们就可以将转移方程简化掉两维,用步数和横坐标来替换纵坐标,如此,转移方程为

ans[k][x1][x2]=max{

ans[k-1][x1-1][x2-1],

ans[k-1][x1][x2],

ans[k-1][x1-1][x2],

ans[k-1][x1][x2-1]

}

步数增加之后,即便横坐标不变,纵坐标必定是增加了的

此题另一个优化点是步数k

因为k状态只和k-1状态有关,我们可以利用滚动数组来进一步优化空间

有个注意的地方是s[i][j]的取值范围(<=10^9),故而初始化的时候要把-inf弄得小一些,1e12足够了,因为这个我还错了几次,供大家借鉴

题目链接→Problem 2234 牧场物语

 Source Code

/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 105;const int M = 40;const __int64 inf = 1e12;const int mod = 2009;int s[N][N];__int64 ans[2][N][N];int main(){    int n,i,j,k,c;    while(~scanf("%d",&n))    {    for(i=0;i<=n;i++)    for(j=0;j<=n;j++)   {   ans[0][i][j]=ans[1][i][j]=-inf;   if(i!=0&&j!=0)scanf("%d",&s[i][j]);      }   ans[0][1][1]=s[1][1];   for(c=0,k=1;k<2*n-1;k++)   {   c^=1;   for(i=1;i<=min(k+1,n);i++)    for(j=1;j<=min(k+1,n);j++)   {ans[c][i][j]=max(max(ans[c^1][i][j],ans[c^1][i-1][j-1]),max(ans[c^1][i-1][j],ans[c^1][i][j-1])); if(i==j)ans[c][i][j]+=s[i][k+2-i];elseans[c][i][j]+=s[i][k+2-i]+s[j][k+2-j];   }   }   printf("%I64d\n",ans[c][n][n]);    }    return 0;}

 Problem G 国王的出游

Accept: 0    Submit: 0
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

黑暗之王有一片棋盘般的疆土,这片疆土有2*10^9行,有2*10^9列。假设这块疆土的行从上到下编号1到2*10^9,它的列从左到右也编号1到2*10^9。我们可以把第i行第j列的方格记为坐标(i,j)。

但是这偌大棋盘只有被给的N个线状区域才是允许通行的。每个线状区域被三个参数描述,ri,ai,bi(ai<= bi)。ri代表给定线状区域的行编号,ai代表线状区域的起始列编号,bi代表末尾编号。

现在国王想要从一个给定坐标方格(x0,y0)通过最小移动步数到达终点坐标方格(x1,y1),而且只能通过上述给出的允许通行的线状区域。

国王移动一步只能发生在相邻的方格之间。此外,如果两个方格至少共享一个点我们便认为他们相邻。

 Input

有多组数据(<=30),处理到文件尾(EOF)。

每组数据第一行包含四个整数,x0, y0, x1, y1(1 <= x0, y0, x1, y1 <= 2*10^9),分别代表国王的初始坐标和终点坐标。

第二行有一个整数N (1 <= N <= 10^5),代表有N条可通行的线状区域。

接下里会有N行,第i行包含三个整数,ri,ai, bi (1 <= ri, ai bi <= 2*10^9),含义看题面。

数据允许线状区域会有交叉或者嵌套。

数据保证国王的起点方格和终点方格都是可通行的,并且两个区域不相同。另外保证所有线状区域的的长度总和不超过10^5。

1 <= x0, y0, x1, y1, ri, ai, bi<= 2*10^9,1 <= N <= 10^5

 Output

如果没有一条道路使得国王从起始区域到达终点区域,则输出 -1。

否则,则输出国王的从起始区域到达终点区域的最小步数。

 Sample Input

5 7 6 11
3
5 3 8
6 7 11
5 2 5

1 1 2 10
2
1 1 3
2 6 10

 Sample Output

4
-1

 Problem Idea

解题思路:一开始相信绝大多数人都会被这棋盘的规模吓一跳,2*10^9 * 2*10^9,然后束手无策,但是只要仔细读题,就会发现其中的入手点

题目保证所有线状区域的的长度总和不超过10^5,原来之前的都是虚张声势

那么,题目就容易下手了,相信绝大多数人这个时候应该都能想到bfs(广搜)

但是难点在于如何保存已经走过的格子,因为坐标规模比较大,之前的visit[][]数组必然是已经开不出来

那我们可以转换一下思路,我最先想到的是用STL里的map来实现

TLE代码(下面有正确代码,往下看)如下:

/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 100005;const int M = 40;const int inf = 100000000;const int mod = 2009;struct node{int x,y,c;node(){}node(int x0,int y0,int c0):x(x0),y(y0),c(c0){}};int g[8][2]={{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}};map<pair<int,int>,bool> m;int bfs(int x0,int y0,int x1,int y1){int i;node u;queue<node> q;q.push(node(x0,y0,0));m[make_pair(x0,y0)]=false;while(!q.empty()){u=q.front();q.pop();for(i=0;i<8;i++){x0=u.x+g[i][0];y0=u.y+g[i][1];if(x0==x1&&y0==y1)                return u.c+1;if(m[make_pair(x0,y0)]){m[make_pair(x0,y0)]=false;q.push(node(x0,y0,u.c+1));}}}return -1;}int main(){int x0,y0,x1,y1,n,i,r,a,b;while(~scanf("%d%d%d%d",&x0,&y0,&x1,&y1)){m.clear();scanf("%d",&n);while(n--){scanf("%d%d%d",&r,&a,&b);for(i=a;i<=b;i++)m[make_pair(r,i)]=true;}if(x0==x1&&y0==y1)            puts("0");        else            printf("%d\n",bfs(x0,y0,x1,y1));}    return 0;}

题目链接→Problem 2235 国王的出游

当然这个想法解决单组已经足够了(详情见CodeForces 242C),但是对于T(<=30)还有点力不从心,原因在于每次的map搜索还是比较耗时的,那我们再进行进一步的优化,考虑将能走的格子坐标保存下来,每次二分来找判断该点是否能走,这样便成功得以解决,正确代码如下:

 Source Code

/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 100005;const int M = 40;const int inf = 100000000;const int mod = 2009;struct node{int x,y,c;node(){}node(int x0,int y0,int c0):x(x0),y(y0),c(c0){}};int p,g[8][2]={{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}};pair<int,int> m[N];bool v[N];int bfs(int x0,int y0,int x1,int y1){int i,k;node u;queue<node> q;q.push(node(x0,y0,0));k=lower_bound(m,m+p,make_pair(x0,y0))-m;if(m[k]==make_pair(x0,y0))v[k]=false;while(!q.empty()){u=q.front();q.pop();for(i=0;i<8;i++){x0=u.x+g[i][0];y0=u.y+g[i][1];if(x0==x1&&y0==y1)                return u.c+1;   k=lower_bound(m,m+p,make_pair(x0,y0))-m;if(m[k]==make_pair(x0,y0)&&v[k]){v[k]=false;q.push(node(x0,y0,u.c+1));}}}return -1;}int main(){int x0,y0,x1,y1,n,i,r,a,b;while(~scanf("%d%d%d%d",&x0,&y0,&x1,&y1)){p=0;memset(v,false,sizeof(v));scanf("%d",&n);while(n--){scanf("%d%d%d",&r,&a,&b);for(i=a;i<=b;i++){m[p]=make_pair(r,i);v[p++]=true;}}sort(m,m+p);p=unique(m,m+p)-m;if(x0==x1&&y0==y1)            puts("0");        else            printf("%d\n",bfs(x0,y0,x1,y1));}    return 0;}


 Problem H 第十四个目标

Accept: 0    Submit: 0
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

目暮警官、妃英里、阿笠博士等人接连遭到不明身份之人的暗算,柯南追踪伤害阿笠博士的凶手,根据几起案件现场留下的线索发现凶手按照扑克牌的顺序行凶。在经过一系列的推理后,柯南发现受害者的名字均包含扑克牌的数值,且扑克牌的大小是严格递增的,此外遇害者与毛利小五郎有关。

为了避免下一个遇害者的出现,柯南将可能遭到暗算的人中的数字按关联程度排列了出来,即顺序不可改变。柯南需要知道共有多少种可能结果,满足受害人名字出现的数字严格递增,但是他柯南要找出关键的证据所在,所以这个任务就交给你了。

(如果你看不懂上面在说什么,这题是求一个数列中严格递增子序列的个数。比如数列(1,3,2)的严格递增子序列有(1)、(3)、(2)、(1,3)、(1,2),共5个。长得一样的但是位置不同的算不同的子序列,比如数列(3,3)的答案是2。)

 Input

多组数据(<=10),处理到EOF。

第一行输入正整数N(N≤100 000),表示共有N个人。

第二行共有N个整数Ai(1≤Ai≤10^9),表示第i个人名字中的数字。

 Output

每组数据输出一个整数,表示所有可能的结果。由于结果可能较大,对1 000 000 007取模后输出。

 Sample Input

3
1 3 2

 Sample Output

5

 Problem Idea

解题思路:找数列中严格递增子序列的个数

记a[i]表示结尾为i的序列个数,考虑到Ai会达到10^9,故而先离散化

然后,我们要计算a[i],必须知道所有的a[j](j<i)之和,这时,可以考虑用树状数组

因为树状数组c[]表示的正是前多少项之和,详情见  链接→树状数组学习笔记

当然也可以自己写线段树

题目链接→Problem 2236 第十四个目标

 Source Code

/*Sherlock and Watson and Adler*/#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>#include<stdlib.h>#include<queue>#include<stack>#include<math.h>#include<vector>#include<map>#include<set>#include<cmath>#include<complex>#include<string>#include<algorithm>#include<iostream>#define exp 1e-10using namespace std;const int N = 100005;const int M = 40;const int inf = 100000000;const int mod = 1000000007;int s[N],a[N],n;__int64 c[N];int lowbit(int t)  {//计算c[t]展开的项数         return t&(-t);    }  void update(int i,__int64 x)  {      while(i<=n)      {          c[i]=(c[i]+x)%mod;           i+=lowbit(i);       }  }  __int64 Sum(int n) //求前n项的和.       {      __int64 sum=0;       while(n>0)      {           sum=(sum+c[n])%mod;            n-=lowbit(n);      }      return sum;  }  int main(){    int i,j,k;    __int64 ans;    while(~scanf("%d",&n))    {    memset(c,0,sizeof(c));    ans=0;    for(i=0;i<n;i++)    {    scanf("%d",&s[i]);    a[i]=s[i];    }    sort(a,a+n);    k=unique(a,a+n)-a;    for(i=0;i<n;i++)    s[i]=lower_bound(a,a+k,s[i])-a+1;   /*for(i=0;i<n;i++)printf("%d ",s[i]);*/for(i=0;i<n;i++)update(s[i],Sum(s[i]-1)+1);/*for(i=1;i<=n;i++)printf("%I64d ",c[i]);*/    printf("%I64d\n",Sum(n));    }    return 0;}

菜鸟成长记

3 0
原创粉丝点击