• Egyptian Collegiate Programming Contest (ECPC 2015)


      题目链接:https://vjudge.net/contest/155219#overview

      A题,用全排列来找出比当前这个数字字典序还大的排列有几个,然后前缀和dp即可。据说可以康拓展开来快速找出前面需要实现的要求。

      B题,水题。

      C题,感觉数据比较水。做法是dsu+lca,但是为了实现lca树的结构不被破坏,dsu::find()不能压缩路径。然后线性找lca没T也是有点神奇。

      D题,dfs即可。

      E题,dp[i][j][k],表示到了(i,j)并且已经吃了k个2,最多吃了几个3的状态。最后遍历一下dp[n][m][...]找出答案即可。

      F题,水题。

      G题,二分答案,然后跑dij即可。很久没写dij,刚开始竟然忘了优先队列要写一个greater..

      H题,不会= =。

      I题,水题。

      J题,刚开始想了一个比较麻烦的方法,不需要考虑dfs的时候当前步是谁的做法,然后WA了。然后加上就可以了,还好写很多。不用记忆化搜索也能够过。

      K题,dp[i][j]表示计算到i位,%p答案是j的种类数。然后滚动数组一下再一位一位dp即可。之所以dp[pre][0]每次都需要先+1是因为,之前是空串也算是一个0,换言之,只有当前这位数字%p是0也是可以的。

      L题,这个博弈论构造起来好麻烦(虽然最终代码很简单)。。首先注意到随着个数的增加,一堆是必胜堆还是必败堆是交替变化的。必胜态考虑完了要考虑必败态的话,必败态的L肯定是上一个必胜态的R+1。然后因为必败态的任意一种选择都是必胜态,因此必败态的最大堆是上一个必胜态的R,然后其他堆都是1,如此不能再多了(因为再多一个,放在最大堆上,最大堆上就变成了必败态,这和必败态的定义不符合),因此这个必败态的R是是上一个必胜态的R+(n-1)。那么再考虑下一个必胜态,必胜态的定义是只要有一种方法能到达必败态即可,因此最大堆是上一个必败态的R,其他的先不妨设置为1,然后同样的考虑再增加一个,因为当前选择者肯定不会傻到放到最大堆上(这样的话最大堆就变成必胜态了),那么只能放到其他堆,这样的状况一直会持续到其他n-1个堆都是n-1(不能再多了,因为n是必胜态),这是临界值,所以这个必胜态的R值是上一个必败态的R再加上(n-1)*(n-1)。如此交替即可,那么给定一个x一定能够知道这是必胜堆还是必败堆。最后对于先手者,只要有一堆是必胜的,选择它即可。

      M题,比赛的时候搞了半天,最后因为写起来好麻烦就放弃了。看了一下仓鼠的代码是dp的,一下子简单许多。那么就直接放一下他的代码好了(他的叉乘判断顺逆时针方法习惯和我的有点不同,但本质是一样的):

     1 #include <bits/stdc++.h>
     2 #define x first
     3 #define y second
     4 using namespace std;
     5 typedef pair<int,int> pii;
     6 const int N = 1000 + 5;
     7 const double inf = 1e18;
     8 
     9 pii p[N];
    10 int st[N], tot;
    11 int n;
    12 double dp[N];
    13 
    14 // k->i, k->j
    15 bool cross(int i,int j,int k)
    16 {
    17     pii pa = pii(p[i].x-p[k].x, p[i].y-p[k].y);
    18     pii pb = pii(p[j].x-p[k].x, p[j].y-p[k].y);
    19     return 1LL*pa.x*pb.y - 1LL*pb.x*pa.y > 0;
    20 }
    21 double dis(int i,int j)
    22 {
    23     double xx = p[i].x - p[j].x;
    24     double yy = p[i].y - p[j].y;
    25     return sqrt(xx*xx + yy*yy);
    26 }
    27 
    28 int main()
    29 {
    30     int T;
    31     cin >> T;
    32     while(T--)
    33     {
    34         scanf("%d",&n);
    35         for(int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y);
    36         dp[0] = 0;
    37         dp[1] = inf;
    38         for(int i=2;i<=n;i++)
    39         {
    40             dp[i] = inf;
    41             tot = 0;
    42 
    43             for(int j=i;j>0;j--)
    44             {
    45                 // 如果储备点大于两个并且存在凹进去的部分,去掉那部分
    46                 while(tot > 1 && cross(j,st[tot-1],st[tot-2])) tot--;
    47                 // 如果只有一个储备点,那么是可以直接更新答案的,要注意的是,中间的线段是可以选择去掉的。
    48                 // 包含第一个点和最后一个点的段是不能被去掉的,所以dp[1]=inf,且dis(i,j)必须存在。
    49                 if(tot == 1) dp[i] = min(dp[i], min(dp[j-1], dp[j]) + dis(i,j));
    50                 st[tot++] = j;
    51             }
    52         }
    53         printf("%.6f
    ",dp[n]);
    54     }
    55     return 0;
    56 }
    M题
  • 相关阅读:
    office 2007 验证失败的解决方法
    google开不了(解决办法)
    Mobilenet V1
    Windows10系统下IDECLion的安装与配置
    单目相机成像过程
    C++中如何在函数中返回局部变量的指针/引用/地址?
    ResNeXt论文阅读笔记.md
    Mobilenet V2
    Xception
    InceptionV4
  • 原文地址:https://www.cnblogs.com/zzyDS/p/6622191.html
Copyright © 2020-2023  润新知