2020ICPC南京【个人题解EFHKLM】

这篇具有很好参考价值的文章主要介绍了2020ICPC南京【个人题解EFHKLM】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

E - Evil Coordinate(思维、暴力)

思路

首先如果炸弹在(0,0)或者机器人最终停在炸弹处,那么一定Impossible。
对于其他的情况,如果存在一条路径使得机器人可以不经过炸弹,那么一定存在一种方案,使得相同的方向在这个方案种是连在一起的。
于是可以直接枚举所有方向的排列,共4!个,判断每一种排列能否绕过炸弹即可。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 100005

using namespace std;

int mx,my;
int n;
char s[N];
int L,R,D,U;
char tmp[4] = {'U','D','L','R'};

bool check(){
    int stx = 0,sty = 0;
    for(int i = 0;i < n;i ++){
        if(stx == mx && sty == my) return false;
        if(s[i] == 'U') sty ++;
        if(s[i] == 'D') sty --;
        if(s[i] == 'L') stx --;
        if(s[i] == 'R') stx ++;
        if(stx == mx && sty == my) return false;
    }
    return true;
}

void solve(){
    L = R = D = U = 0;
    scanf("%d%d",&mx,&my);
    scanf("%s",s);
    n = strlen(s);

    for(int i = 0;i < n;i ++)
        if(s[i] == 'U') U ++;
        else if(s[i] == 'L') L ++;
        else if(s[i] == 'R') R ++;
        else if(s[i] == 'D') D ++;

    sort(tmp,tmp + 4);
    do{
        int cnt = 0;
        for(int i = 0;i < 4;i ++){
            if(tmp[i] == 'U'){
                for(int j = 0;j < U;j ++)
                    s[cnt++] = 'U';
            }
            else if(tmp[i] == 'D'){
                for(int j = 0;j < D;j ++)
                    s[cnt++] = 'D';
            }
            else if(tmp[i] == 'L'){
                for(int j = 0;j < L;j ++)
                    s[cnt++] = 'L';
            }
            else if(tmp[i] == 'R'){
                for(int j = 0;j < R;j ++)
                    s[cnt++] = 'R';
            }
        }
        s[cnt] = '\0';
        if(check()){
            printf("%s\n",s);
            return;
        }
    }while(next_permutation(tmp,tmp + 4));
    puts("Impossible");
}

int main(){
    int T;scanf("%d",&T);
    while(T --)
        solve();
    return 0;
}

F - Fireworks(概率期望、三分)

思路

假设一开始,最优解为做 x x x个烟花之后放掉,那么如果放了之后没有一个好烟花,状态又回到了初始状态,于是此时也会继续做 x x x个烟花然后放掉,于是若采取最优策略,每一次做的烟花数量是一定的。
考虑期望如何计算,令做 x x x个烟花之后放掉为一轮,那么每一轮产生好烟花的概率为 1 − ( 1 − p ) x 1-(1-p)^x 1(1p)x,产生好烟花的分布为几何分布,其轮数期望为 1 1 − ( 1 − p ) x \frac{1}{1-(1-p)^x} 1(1p)x1,由于每一轮所需时间为 x n + m xn + m xn+m,于是期望时间为 x n + m 1 − ( 1 − p ) x \frac{xn+m}{1-(1-p)^x} 1(1p)xxn+m
可以求二阶导证明该时间为关于 x x x的单峰函数,用三分求极值即可。注意要满足 x x x为整数,三分我用的是浮点数,最后上取整与下取整取个最小值即可。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <cmath>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const double esp = 1e-6;

int n, m;
double p;

double f(double x)
{
    return (x * n + m) / (1 - pow(1 - p, x));
}

double solve()
{
    double l = 1, r = 1e9;
    while (r - l > esp)
    {
        double mid = (l + r) / 2;
        double mmid = (mid + r) / 2;
        if (f(mid) < f(mmid))
            r = mmid;
        else l = mid;
    }
    return min(f((int)l), f((int)l + 1));
}   

int main()
{
    #ifdef ZYCMH
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    #endif
    int _; scanf("%d", &_);
    while (_ --)
    {
        scanf("%d%d%lf", &n, &m, &p);
        p /= 10000;
        printf("%.10f\n", solve());
    }
    return 0;
}

H - Harmonious Rectangle(思维、暴力)

思路

首先,如果n==1 || m == 1那么答案为0
如果n >= 8 || m >= 8那么答案为 3 n m 3^{nm} 3nm(思考一下即可知道为什么)
对于2<=n,m<=7的矩形而言,我们可以用dfs搜索出所有的答案,然后打个表。
如果直接暴搜的话,估计等一年都等不出结果(复杂度为 3 n m 3^{nm} 3nm),我们可以每次判断一下当前是否存在一组符合条件的点对,如果存在则直接返回的当前的方案数(后面每个点无论涂什么颜色都可以了),这样剪枝之后搜得很快。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <cmath>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int mod = 1000000007;

int n, m;
int a[10][10];
int id[10][10];

int res[] = {15,339,4761,52929,517761,4767849,339,16485,518265,14321907,387406809,460338013,4761,518265,43022385,486780060,429534507,792294829,52929,14321907,486780060,288599194,130653412,748778899,517761,387406809,429534507,130653412,246336683,579440654,4767849,460338013,792294829,748778899,579440654,236701429};

int qpow(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1) res = 1LL * res * a % mod;
        b >>= 1;
        a = 1LL * a * a % mod;
    }
    return res;
}

int main()
{
    #ifdef ZYCMH
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    #endif
    int _; scanf("%d", &_);
    while (_ --)
    {
        scanf("%d%d", &n, &m);
        if (n == 1 || m == 1) puts("0");
        else if (n >= 8 || m >= 8) 
            printf("%d\n", qpow(3, n * m));
        else{
            for (int i = 2, k = 0; i <= 7; i ++ )
                for (int j = 2; j <= 7; j ++, k ++ )
                    if (i == n && j == m)
                    {
                        printf("%d\n", res[k]);
                        break;
                    }
        }   
    }
    return 0;
}

K - K Co-prime Permutation(签到、构造)

思路

k==0,无解。
否则,直接把前 k k k个数左移一位即可。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <cmath>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

int main()
{
    #ifdef ZYCMH
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    #endif
    int n, k;
    scanf("%d%d", &n, &k);
    if (k == 0) puts("-1");
    else 
    {

        for (int i = 1; i < k; i ++ )
            printf("%d ", i + 1);
        printf("1 ");
        for (int i = k + 1; i <= n; i ++ )
            printf("%d ", i);
        puts("");
    }
    return 0;
}

L - Let’s Play Curling(签到)

思路

本质上就是求每两个蓝色冰壶中有多少个红色冰壶,排序之后二分即可。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <cmath>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 100005;

int n, m;
int a[N], b[N];

int main()
{
    #ifdef ZYCMH
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    #endif
    int _; scanf("%d", &_);
    while (_ --)
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i ++ )
            scanf("%d", &a[i]);
        for (int i = 1; i <= m; i ++ )
            scanf("%d", &b[i]);
        b[m + 1] = -2e9, b[m + 2] = 2e9;
        m += 2;
        sort(a + 1, a + 1 + n);
        sort(b + 1, b + 1 + m);
        int ans = 0;
        for (int i = 1; i < m; i ++ )
        {
            int l = upper_bound(a + 1, a + 1 + n, b[i]) - a;
            int r = lower_bound(a + 1, a + 1 + n, b[i + 1]) - a - 1;
            ans = max(r - l + 1, ans);
        }
        if (!ans) puts("Impossible");
        else printf("%d\n", ans);
    }
    return 0;
}

M - Monster Hunter(树形背包)

思路

挺经典的树形背包题目。
我们可以用魔法⭐打败怪兽👾哟。
因为怪兽构成了一个树形结构,因此可以先考虑每棵子树的情况。对于某个节点的怪兽,可以选择是否使用魔法攻击,因此可以开一个维0/1来表示(常规操作)。每棵子树还能使用不同的魔法次数,这个状态也需要记录。
因此可用 d p [ u ] [ i ] [ j ] [ f l ] dp[u][i][j][fl] dp[u][i][j][fl]表示为以 u u u为根的子树中,枚举到第 i i i棵子树,( i = 0 i = 0 i=0表示子树根本身),使用了 j j j次魔法, u u u怪兽使用( f l = 1 fl = 1 fl=1)或不使用( f l = 0 fl=0 fl=0)的最小代价。当然枚举到第 i i i棵子树这一维可以通过 j j j的倒序枚举优化掉。
先考虑 u u u本身,而不考虑 u u u的孩子,则 d p [ u ] [ 0 ] [ 0 ] [ 0 ] = h p [ u ] , d p [ u ] [ 0 ] [ 1 ] [ 1 ] = 0 dp[u][0][0][0] = hp[u],dp[u][0][1][1] = 0 dp[u][0][0][0]=hp[u]dp[u][0][1][1]=0
再考虑 u u u的孩子:

  1. u u u是用魔法打败的,那么顺序必然为先用魔法打败 u u u,再去打 u u u的孩子,这样 u u u的孩子可以选择不用魔法。
  2. u u u不是用魔法打败的,最终打败 u u u的代价还需要加上其孩子中不使用魔法的 h p hp hp,因为这些不使用魔法打败的孩子,必然是在 u u u被打败之后才被打败,即 u u u未被打败的时候这些孩子还存活

于是,状态转移方程可以写为:
d p [ u ] [ i ] [ j ] [ 1 ] = m i n v ( d p [ v ] [ s i z [ v ] ] [ k ] [ 0 ] , d p [ v ] [ s i z [ v ] ] [ k ] [ 1 ] ) + d p [ u ] [ i − 1 ] [ j − k ] [ 1 ] dp[u][i][j][1] = min_v(dp[v][siz[v]][k][0], dp[v][siz[v]][k][1])+dp[u][i-1][j-k][1] dp[u][i][j][1]=minv(dp[v][siz[v]][k][0],dp[v][siz[v]][k][1])+dp[u][i1][jk][1]
d p [ u ] [ i ] [ j ] [ 0 ] = m i n v ( d p [ v ] [ s i z [ v ] ] [ k ] [ 0 ] + v a l [ v ] , d p [ v ] [ s i z [ v ] ] [ k ] [ 1 ] ) + d p [ u ] [ i − 1 ] [ j − k ] [ 0 ] dp[u][i][j][0] = min_v(dp[v][siz[v]][k][0]+val[v], dp[v][siz[v]][k][1])+dp[u][i-1][j-k][0] dp[u][i][j][0]=minv(dp[v][siz[v]][k][0]+val[v],dp[v][siz[v]][k][1])+dp[u][i1][jk][0]
由于第二维可以优化掉,于是方程可写为:
d p [ u ] [ j ] [ 1 ] = m i n v ( d p [ v ] [ k ] [ 0 ] , d p [ v ] [ k ] [ 1 ] ) + d p [ u ] [ j − k ] [ 1 ] dp[u][j][1] = min_v(dp[v][k][0], dp[v][k][1])+dp[u][j-k][1] dp[u][j][1]=minv(dp[v][k][0],dp[v][k][1])+dp[u][jk][1]
d p [ u ] [ j ] [ 0 ] = m i n v ( d p [ v ] [ k ] [ 0 ] + v a l [ v ] , d p [ v ] [ k ] [ 1 ] ) + d p [ u ] [ j − k ] [ 0 ] dp[u][j][0] = min_v(dp[v][k][0]+val[v], dp[v][k][1])+dp[u][j-k][0] dp[u][j][0]=minv(dp[v][k][0]+val[v],dp[v][k][1])+dp[u][jk][0]
代码中需要优化掉一些不合法的状态才能通过这道题。文章来源地址https://www.toymoban.com/news/detail-407377.html

代码

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 2e3 + 10;
int hp[N];
LL f[N][N][2], inf = 1e18;
int n;
//f[u][j][0/1]表示以u为子树的根,使用了i次魔法,u使用魔法(1) u不使用魔法(0) 的最小代价
//考虑更新f[u][j][1],需要对子树进行背包:有j次魔法的使用机会去分配给子树,根已被消除,孩子使用或不使用魔法均可
/*
1
5
1 2 3 4
1 2 3 4 5
*/
struct E
{
    int nxt, to;
}e[N];
int head[N], cnt, sz[N];
void add(int u, int v)
{
    e[++ cnt] = {head[u], v};
    head[u] = cnt;
}
inline LL min_(LL a, LL b)
{
    if(a > b) return b;
    return a;
}
void dfs(int u)
{
    sz[u] = 1, f[u][0][0] = hp[u], f[u][1][1] = 0;
    for(int i = head[u]; i; i = e[i].nxt)
    {
        int v = e[i].to;
        dfs(v);
        sz[u] += sz[v];
        for(int j = sz[u]; j >=0; j --)
        {
            LL cur1 = inf, cur2 = inf;
            for (int k = min_(j, sz[v]); k >= 0; k -- )
            {
                if (j - k > sz[u] - sz[v]) break; //!!!优化掉一些不合法的状态
                cur1 = min_(cur1, f[u][j - k][0] + min_(f[v][k][0] + hp[v], f[v][k][1]));
                cur2 = min_(cur2, f[u][j - k][1] + min_(f[v][k][0], f[v][k][1]));
            }
            f[u][j][0] = cur1, f[u][j][1] = cur2;
        }
    }
}
void solve()
{
    cnt = 0;
    scanf("%d", &n);    
    for(int i = 0; i <= n; i ++) head[i] = 0;
    for(int i = 0; i <= n; i ++) for(int j = 0; j <= n; j ++) f[i][j][0] = f[i][j][1] = inf;
    for(int i = 2; i <= n; i ++)
    {
        int p; scanf("%d", &p);
        add(p, i);
    }
    for(int i = 1; i <= n; i ++) scanf("%d", &hp[i]);
    dfs(1);
    for(int i = 0; i <= n; i ++) printf("%lld ", min_(f[1][i][0], f[1][i][1]));
    puts("");
}
int main()
{
    int T; scanf("%d", &T);
    while(T --)
        solve();
}

到了这里,关于2020ICPC南京【个人题解EFHKLM】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 2020第11届蓝桥杯矩阵的真·详细题解

    本文章主要面向算法初学者、蓝桥杯参赛者。 今天重温一手第十三届的统计子矩阵,搜索的时候发现了第十一届的矩阵题,然后看了下网上的文章,好家伙! 全网对于此题没有高质量的文章和解答思路,全是贴个代码最多说一两句别的话,代码里注释都没几行,对于初学者

    2023年04月16日
    浏览(40)
  • 【C++题解】[CSP-J2020]优秀的拆分

    P a r t Part P a r t 1 1 1 读题 题目描述 一般来说,一个正整数可以拆分成若干个正整数的和。 例如: 1 = 1 1=1 1 = 1 , 10 = 1 + 2 + 3 + 4 10=1+2+3+4 10 = 1 + 2 + 3 + 4 等。对于正整数 n n n 的一种特定拆分,我们称它为“优秀的”,当且仅当在这种拆分下, n n n 被分解为了若干个不同的 2

    2024年02月06日
    浏览(45)
  • P8719 [蓝桥杯 2020 省 AB2] 字串排序题解

    根据题目意思,我们构造的字符串需要满足冒泡排序交换次数为 V V V 次,越短越好的情况下输出字典序最小的那一个。于是我们先从字符串的长度开始考虑。 如何求字符串的最短长度是多少 设字符串长度为 l e n len l e n , 若该长度的字符串能构造出的最大交换数 ≥ V geq V ≥

    2024年02月08日
    浏览(63)
  • 【超好懂的比赛题解】2021CCPC哈尔滨站 个人题解

    title : 2021CCPC哈尔滨站 题解 date : 2022-10-4 tags : ACM,题解,练习记录 author : Linno 题目链接:https://codeforces.com/gym/103447 补题进度:7/12 B-Magical Subsequence A的值很小,我们直接枚举两个数的和,然后遍历一遍看看能凑多少对,记录最长的答案即可。map应该会超时,建议用前缀和。 C

    2023年04月08日
    浏览(50)
  • 【超好懂的比赛题解】2022CCPC四川省赛 个人题解

    title : “海康威视杯“ 2022年第十四届四川省大学生程序设计大赛 tags : ACM,练习记录 date : 2022-10-18 author : Linno 题目链接:https://ac.nowcoder.com/acm/contest/42105 出题数量:5/11 (顺序:K-F-B-H-A) A-Adjacent Swapping 显然可以先贪心移动(直接扫一遍)字符使前后两半在字符数量上是一样的

    2024年02月08日
    浏览(61)
  • LeetCode 第385场周赛个人题解

    目录 100212. 统计前后缀下标对 I 原题链接 题目描述 接口描述 思路分析 代码详解 100229. 最长公共前缀的长度 原题链接 题目描述 接口描述 思路分析 代码详解 100217. 出现频率最高的素数 原题链接 题目描述 接口描述 思路分析 代码详解 100212. 统计前后缀下标对 II 原题链接 题目

    2024年02月19日
    浏览(77)
  • LeetCode 第 388 场周赛个人题解

    目录 100233. 重新分装苹果 原题链接 思路分析 AC代码 100247. 幸福值最大化的选择方案 原题链接 思路分析 AC代码 100251. 数组中的最短非公共子字符串 原题链接 思路分析 AC代码 100216. K 个不相交子数组的最大能量值 原题链接 思路分析 AC代码 100233. 重新分装苹果 直接模拟 降序排

    2024年03月15日
    浏览(61)
  • 2018年第三届 美亚杯电子取证 个人赛题解

    取证直接获取 FC20782C21751BA76B2A93F3A17922D0 E 查看硬盘个数 3个 C LBA开始地址 我们首先确定操作系统分区 可以发现是 E 盘 然后我们开始看物理地址 物理位置:32,213,303,296 除以 512 答案为D 这里就是需要通过扇区x512来计算 答案为E C 这里真不会 看了看 主要是看 磁盘十六进制 第1

    2024年02月06日
    浏览(35)
  • 2023蓝桥杯大学A组C++决赛游记+个人题解

    发烧了一晚上没睡着,感觉鼻子被打火机烧烤一样难受,心情烦躁 早上6点起来吃了个早饭,思考能力完全丧失了,开始看此花亭奇谭 看了六集,准备复习数据结构考试,然后秒睡 一睁眼就是下午2点了 挂了个毛概课串讲,点了个外卖,吃完又睡着了 醒来就晚上8点了 然后又

    2024年02月08日
    浏览(89)
  • 2023第十四届蓝桥杯Java B组个人题解

    欢迎大家阅读蓝桥杯文章专栏🍄🍄 🔥 2023第十四届蓝桥杯模拟赛第二期个人题解(Java实现 ) 🔥 2023第十四届蓝桥杯模拟赛第三期个人题解(Java实现 ) 🔥 蓝桥杯备赛之动态规划篇——背包问题 🔥 蓝桥杯备赛之动态规划篇——涂色问题(区间DP) 🔥 蓝桥杯真题——单词

    2023年04月15日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包