【ACM算法竞赛日常训练】DAY1题解与分析

皇甫嵩
• 阅读 666

DAY1 共四题:

视频教程:https://www.bilibili.com/video/BV1JX4y1d7LC

🎈 作者:Eriktse
🎈 简介:19岁,211计算机在读,现役ACM银牌选手🏆力争以通俗易懂的方式讲解算法!❤️欢迎关注我,一起交流C++/Python算法。(优质好文持续更新中……)🚀
🎈 个人博客:www.eriktse.com

月月查华华的手机

  • tag:动态规划
注意:子序列是可以分隔开的,请区别子串和子序列。

我们定义dp[i][j]表示在母串中第i右边最近的字符j的位置,如果不存在则为-1,初始值为-1

倒序处理出dp数组后,对每一个子串进行判断。

判断的思路是:当前在母串的位置j,当前子串字符为k,那么我找dp[j][k]看看是否存在,如果有就让j跳过去,如果没有就说明母串中不存在这个子序列。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e6 + 9;

char s[maxn];

int dp[maxn][30], now[30];//dp[i][j]表示母串中第i位右边最近的字符j的位置

signed main()
{
    memset(dp, -1, sizeof dp);
    memset(now, -1, sizeof now);
    
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    
    for(int i = n;i >= 0; -- i)
    {
        for(int j = 0;j < 26; ++ j)dp[i][j] = now[j];
        if(i > 0)now[s[i] - 'a'] = i;
    }
    
    int T;scanf("%lld", &T);
    while(T --)
    {
        scanf("%s", s + 1);
        int m = strlen(s + 1);
        bool ans = true;
        
        for(int i = 1, j = 0;i <= m; ++ i)
        {
            int k = s[i] - 'a';
            
            if(dp[j][k] == -1)
            {
                ans = false;
                break;
            }
            else j = dp[j][k];
        }
        printf("%s\n", ans ? "Yes" : "No");
    }
    
    return 0;
}

Rinne Loves Edges

  • tag:简单图论,树

首先以s作为根构造一棵树,fa[x]表示x的父亲。

不难发现,假如我要使得点x为根的子树的所有叶子到不了s,那么可以通过删除x - fa[x]这条边或删除x与所有儿子节点的边,那么对于x的儿子亦是如此。

所以我们就有了dp方程, w[x]xfa[x]的边权,dp[x]表示删除点x的最小代价(代码中的dp用dfs表示):

$$dp[x] = min(w[x], \sum_{y \in g[x] y \neq fa[x]}w[y])$$

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 9, inf = 1e18;

int w[maxn];//w[i]表示点i的父亲这条边的边权

vector<pair<int, int> > g[maxn];

int n, m, s;

int dfs(int x, int pre)//pre是x的父节点,返回删除节点x和父亲这条边的最小代价
{
    //有可能是根
    if(g[x].size() == 1 and x != s)return w[x];
    
    int res = 0;//res表示子节点的代价之和
    for(auto &i : g[x])
    {
        int y = i.first, dw = i.second;
        //x -> y, cost = dw
        if(y == pre)continue;//如果下一个点是当前的父节点,就不进入
        w[y] = dw;
        res += dfs(y, x);
    }
    return min(w[x], res);
}

signed main()
{
    scanf("%lld %lld %lld", &n, &m, &s);
    for(int i = 1;i <= m; ++ i)
    {
        int x, y, w;scanf("%lld %lld %lld", &x, &y, &w);
        g[x].push_back({y, w});
        g[y].push_back({x, w});
    }
    w[s] = inf;
    printf("%lld\n", dfs(s, 0));
    return 0;
}

逆序对

  • tag:组合数学

我们知道只有(1, 0)这样的二元组会对答案产生贡献,那么我们枚举第二位,然后找左边有多少个1即可。

对于第i位,左边有$2^{i - 1}$种情况,每种情况平均都是$\frac{i-1}{2}$个1,然后右边有$2^{n - i}$种情况,会使得区间[0, i]的情况重复多次。

所以第i位的贡献$a_i = 2^{n-1} \times \frac{i-1}{2} = 2 ^ {n-2} \times (i - 1) $。

答案是:

$$ ans=\sum_{i=1}^{n}a_i=2^{n-2}\sum_{i=1}^{n}(i-1)=2^{n-3}\times n\times (n-1) $$

#include "bits/stdc++.h"

#define int long long

const int mod = 1e9 + 7;

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

int mo(int x){return (x % mod + mod) % mod;}

signed main() {
    int n; std::cin >> n;
    if (n == 1) {
        std::cout << 0 << "\n";
    } else if (n == 2) {
        std::cout << 1 << "\n";
    } else {
        int m = n % mod;
        std::cout << mo(m * m % mod - m) * ksm(2, n - 3) % mod;        
    }
    return 0;
}

Xorto

  • tag: map, vector, 异或xor

将所有的异或和结果存到一个map里,每一个异或和结果对应一个vector,里面存下了所有的异或和相同的区间,且按照左端点为第一关键字,右端点为第二关键字的顺序排列好。

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn = 1e5 + 9;
int a[maxn];

unordered_map<int, vector<pair<int, int>> > mp;

signed main()
{
    int n;scanf("%lld", &n);
    for(int i = 1;i <= n; ++ i)scanf("%lld", a + i);
    
    for(int i = 1;i <= n; ++ i)
    {
        int xorsum = 0;
        for(int j = i;j <= n; ++ j)
        {
            xorsum ^= a[j];
            if(!mp.count(a[j]))mp[a[j]] = vector<pair<int, int> >();
            
            mp[xorsum].push_back({i, j});
        }
    }
    
    
    int ans = 0;
    
    for(auto &it : mp)
    {
        vector<pair<int, int> >& v = it.second;
        //v中的所有区间,异或和都相同
        for(int i = 0;i < v.size(); ++ i)
        {
            int j = upper_bound(v.begin(), v.end(), v[i].second, 
                                [](const int &x, const pair<int, int>& p)
                                {
                                    return x < p.first;
                                }) - v.begin();
            ans += v.size() - j;
        }
    }
    printf("%lld\n", ans);
    return 0;    
}
🎈 本文由eriktse原创,创作不易,如果对您有帮助,欢迎小伙伴们点赞👍、收藏⭐、留言💬
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这