首页 > 其他分享 >2024牛客暑期多校训练营7 DK

2024牛客暑期多校训练营7 DK

时间:2024-08-11 15:08:47浏览次数:5  
标签:index DK int 多校 2024 ++ ans 区间 define

来源:2024牛客暑期多校训练营7

做题时间:2024_08_06

D Interval Selection

标签:线段树、[[扫描线]]、枚举

题意

区间的每个数字的数量是 \(k\) 的定义为好区间

比如 \(k=2\),数组为 \({1,1,2,3,2,3,1}\) 对于\([3,6]\)和\([1,6]\) 等都符合要求(下标从1开始)

求所有好区间的数量,比如案例中有4个好区间

思路

考虑[[枚举]] \(i\) 点为好区间的左端点,数有多少个右区间是符合要求的

比较好想到的是考虑相同的数字存在数组里

根据k每次可以找到当前i往右的第 \(k\) 个与 \(a_i\) 相同的数的位置

一开始我想过每次到i,然后找区间内其他数的对应位置

比如 \({1,1,2,3,2,3,1}\) ,i为2,那么从i开始,后一个数是2,对于2,其合法区间是 \([5,7]\),因为 \(a_5\) 是2往右的第k个2,并且后续没有2了,所以从5到最后都是2的合法区间

那么 \(a_2,a_3\) 的合法区间的交集是\([7]\)

继续考虑 \(a_4\) 合法区间是\([6,7]\) ,交集同样是 \([7]\)

这就是以 \(a_2\) 为左端点的好区间的选择范围,只有1个

但是枚举肯定是不行,让我们思考,如果以 \(a_i\) 为左端点,现在需要找范围内跟其他所有其他数字的合法区间的交集的长度

其实想到区间就可以尝试用[[线段树]]了

但是用线段树前还需要明确怎么计算好区间

比如 \({1,1,2,3,2,3,1}\),\(i=2\) ,现在只需要找 \(a_3,a_4\) 的往右的第k个相同的数的位置,而不需要考虑 \(a_5,a_6\)

所以i更新后,需要撤销之前的 \(a_i\) 的合法区间的处理,就像i=2处理后,i=1时撤销i=2的操作

以上就是看题解和讲解后的逆推思考,当时想用差分或者线段树但是没想出来,扫描线的套路

代码

具体实施流程

将合法区间置 \(0\),不合法区间 \(+1\),每次处理完i的合法区间后,数 \([i,n]\) 有多少\(0\),就是当前 \(i\) 的贡献

线段树维护区间min,如果区间min为0向下查询,反之结束这层查询

AC代码

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

#define YES "Yes"
#define NO "No"
#define F first
#define S second
#define int long long
#define ull unsigned long long
#define endl "\n"
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define rep(i, j, k) for (i = (j); i < (k); i++)
#define all(x) x.begin(), x.end()
#define vi vector<int>
typedef pair<int, int> pii;

const int MOD = 1000000007;
const int N = 2e5 + 10;
int ii, jj, kk;

int n, k, t;
int mi[N << 2] = {0};
int add[N << 2] = {0};
int cnt[N << 2] = {0}; // 存储区间内0的个数

int a[N];
map<int, vector<int>> mp;

void up(int i) {
    mi[i] = min(mi[i << 1], mi[(i << 1) | 1]);
    cnt[i] = 0;
    if (mi[i] == mi[i << 1]) cnt[i] += cnt[i << 1];
    if (mi[i] == mi[(i << 1) | 1]) cnt[i] += cnt[(i << 1) | 1];
}

void lazy(int i, int jobv) {
    add[i] += jobv;
    mi[i] += jobv;
}

void down(int i) {
    lazy(i << 1, add[i]);
    lazy((i << 1) | 1, add[i]);
    add[i] = 0;
}

void build(int l, int r, int i) {
    if (l == r) {
        mi[i] = 0;
        add[i] = 0;
        cnt[i] = 1;
        return;
    }
    int mid = l + r >> 1;
    build(l, mid, i << 1);
    build(mid + 1, r, (i << 1) | 1);
    up(i);
    add[i] = 0;
}

void func_add(int jobl, int jobr, int jobv, int l, int r, int i) {
    if (jobl > jobr) return;
    if (jobl <= l && r <= jobr) {
        lazy(i, jobv);
        return;
    }

    down(i);
    int mid = r + l >> 1;
    if (jobl <= mid) func_add(jobl, jobr, jobv, l, mid, i << 1);
    if (jobr >= mid + 1) func_add(jobl, jobr, jobv, mid + 1, r, (i << 1) | 1);
    up(i);
}

int query(int jobl, int jobr, int l, int r, int i) {
    if (jobl <= l && r <= jobr) {
        if (mi[i] == 0)
            return cnt[i];
        else
            return 0;
    }
    int mid = r + l >> 1;
    down(i);
    int ans = 0;
    if (jobl <= mid) ans += query(jobl, jobr, l, mid, i << 1);
    if (jobr >= mid + 1) ans += query(jobl, jobr, mid + 1, r, (i << 1) | 1);
    up(i);
    return ans;
}

void solve() {
    mp.clear();
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        mp[a[i]].push_back(i);
    }
    build(1, n, 1);

    int ans = 0;
    map<int, vector<pii>> mp_op; // 用于存储对应数字的操作
    for (int i = n; i >= 1; i--) {
        // 撤回
        auto &v = mp_op[a[i]];
        for (auto &[l, r] : v) {
            func_add(l, r, -1, 1, n, 1);
        }
        v.clear();

        auto &it = mp[a[i]];

        int ki = lower_bound(all(it), i) - it.begin() + k - 1; // i往右的第k个相同数在map中的下标
        // 说明 第k个数不存在
        if (ki >= it.size()) {
            func_add(i, n, 1, 1, n, 1);
            mp_op[a[i]].push_back({i, n});
        } else {
            // 往右第k个数存在
            if (i <= it[ki] - 1) {
                func_add(i, it[ki] - 1, 1, 1, n, 1);
                mp_op[a[i]].push_back({i, it[ki] - 1});
            }
            // 现在处理往右第k+1个数
            if (ki + 1 < it.size() && it[ki + 1] <= n) {
                func_add(it[ki + 1], n, 1, 1, n, 1);
                mp_op[a[i]].push_back({it[ki + 1], n});
            }
        }
        int q = query(i, n, 1, n, 1);
        ans += q;
    }
    cout << ans << endl;
}

signed main() {
    IOS;
    int t;
    for (cin >> t; t; t--)
        solve();
    return 0;
}

K Strings, Subsequences,Reversed Subsequences,Prefixes

标签:字符串

题意

给 \(S\) 和 \(T\) 串,在 \(S\) 串中找到一个子序列,使这个序列的前缀为 \(T\) 逆转后的前缀也为 \(T\)

数一共有几个子序列

题目案例:

input:
7 2
abababaab
ab

output:
8

explain:
"aba","abba","ababa","abbba","abaaba","ababba","abbaba","abababa"

思路

首先,我们先在S串中取最前的T串,再取最后的T串

例如 \(S=abegfhbca\), \(T=ab\),此时贪心取到的最短前缀为 \(ab\),最短后缀为 \(bca\)

那么现在只需要对中间的 \(egfh\) 取本质不同的子序列就可以,也就是[[动态规划]]

还需注意对于特殊情况,也就是 \(aba\) 这种前缀和后缀相交的情况

可知这种都是回文[[字符串]]

比如 \(efabab\) ,最后一个a和最后一个b都可以作为回文中心

那么就去找 \(babafe\) 的后缀是否能接在 \(efabab\) 后面形成回文串,就是找位置

代码

#include <iostream>
#include <string>
#include <vector>

using namespace std;
#define int long long
const int MOD = 1e9 + 7;
int n, m;
string s, t;
vector<int> index[2]; // index[i][j] i表示从前往后或者从后往前,j表示t[j]  整体表示在s串中的最左位置或最右位置

//传入s和s的开始和结束位置,计算本质不同的子序列个数
// 详情参考:https://www.cnblogs.com/ptno/p/16541669.html
int getDifStrNum(string &s, int be, int en) {
    if (be > en) return 1;// 此处表示如果出现 s=gggg t=gg这类,中间只有空串
    vector<int> last(26, -1); // 上一个对应字符的位置
    vector<int> dp(en - be + 2, 0);
    for (int i = be; i <= en; i++) {
        dp[i - be + 1] = dp[i - be] * 2 + 1;
        if (last[s[i] - 'a'] != -1) dp[i - be + 1] -= dp[last[s[i] - 'a'] - be] + 1;
        last[s[i] - 'a'] = i;

        dp[i - be + 1] = (dp[i - be + 1] + MOD) % MOD;
    }
    return dp[en - be + 1] + 1;// 返回时加1,表示空字符串
}

// 马拉车处理
string deal(string &t) {
    string str = "#";
    for (auto &i : t) (str += i) += '#';
    return str;
}

int solve() {
    cin >> n >> m;
    cin >> s >> t;
    index[0].resize(m, 0);
    index[1].resize(m, 0);

    int i = 0, j = 0;
    while (i < s.length() && j < t.length()) {
        if (s[i] == t[j]) {
            index[0][j] = i;
            i++, j++;
        } else
            i++;
    }
    if (j != t.length()) return 0;

    i = n - 1, j = 0;
    while (i >= 0 && j < t.length()) {
        if (s[i] == t[j]) {
            index[1][j] = i;
            i--, j++;
        } else
            i--;
    }
    if (j != t.length()) return 0;

    int ans = 0;
    if (index[0][m - 1] < index[1][m - 1]) ans += getDifStrNum(s, index[0][m - 1] + 1, index[1][m - 1] - 1);

	//马拉车算法处理中心
    string newt = deal(t);
    int len = newt.length(), c = 0;
    vector<int> r(len, 0);
    for (int i = 1; i < len; i++) {
        if (c + r[c] >= i) r[i] = min(r[2 * c - i], c + r[c] - i);
        while (i - r[i] >= 0 && newt[i - r[i]] == newt[i + r[i]]) r[i]++;
        r[i]--;
        if (c + r[c] < i + r[i]) c = i;
    }
	
    for (int i = 0; i < len - 1; i++) {
		if (i + r[i] == len - 1)
            if (i - m == 0 || index[1][i - m - 1] > index[0][m - 1])
                ans++;
    }

    return ans;
}

signed main() {
    cout << solve();
    return 0;
}

标签:index,DK,int,多校,2024,++,ans,区间,define
From: https://www.cnblogs.com/lulaalu/p/18353459

相关文章

  • 注册无需窗口全局常用热键快捷键 2024年8月11日
    注册无需窗口全局常用热键快捷键2024年8月11日 注册无需窗口全局常用热键快捷键2024年8月11日REMC:\Prog\_一键打包成exe\一键打包成exe.batREM此批处理脚本文件最后编辑日期2022年9月23日set/ay=%date:~,4%,mo=1%date:~5,2%%%100,d=1%date:~8,2%%%100,h=%time:......
  • Java最新面试题2024,Java八股文2024
    一.基础篇1.Java语言特点1、简单易学、有丰富的类库2、面向对象(Java最重要的特性,让程序耦合度更低,内聚性更高)3、与平台无关性(JVM是Java跨平台使用的根本)4、可靠安全5、支持多线程2.面向对象和面向过程的区别面向过程:是分析解决问题的步骤,然后用函数把这些步骤一步......
  • 新手常见错误:Language level is invalid or missing in pom.xml. Current project JDK
    目录Blue留声机:分析报错 Blue留声机:今天开一个maven的时候遇到这样一个报错,这个报错对于我来言是一个并不陌生的报错,早期学习spring框架的时候,遇到过这个问题,当时怎么也弄不出来(现在想想那个时候的我真菜),现在却对这种问题的解决游刃有余。好了,不多bb了,看看我一般处理bu......
  • FMS 2024: 带来哪些存储技术亮点?
    这几天,存储界的全球盛会2024FutureofMemoryandStorage(FMS)大会正在大洋彼岸如火如荼进行中(8/6-8/8),大会上又有哪些存储技术亮点,让我们先快速了解下,后续Keynote材料公开后,小编再进行细细解读。亮点1:NVME协议SPEC2.1版本更新NVMExpress组织在2024年8月7日宣布了三......
  • 2024/08/11 每日一题
    LeetCode1035不相交的线方法1:动态规划classSolution{publicintmaxUncrossedLines(int[]nums1,int[]nums2){intn=nums1.length,m=nums2.length;int[][]dp=newint[n+1][m+1];for(inti=1;i<=n;i++){......
  • Linux:@2024-08-10 最新的Openssl-3.3.1 Openssh-9.8p1 Centos7上的编译后二进制 一键
     附件:Portable_Openssl-Openssh9.8p1-bin-el7.v1.2.1.tgz.zip特点:适用于centos7.x 已经编译为二进制对老版本的关键二进制文件sshd、sftp、scp、openssl进行了备份升级前,自动打开一个端口为2222的老版本的sshd服务,你可以连接那个2222的服务,以防死翘翘。对sshd_config进......
  • 2024杭电多校第7场
    71007创作乐曲(hdu7511)妙妙dp题,赛时wyq一眼丁真、发现可以线段树优化dp,可惜我们没推出关键性质,最后TLE遗憾离场。在每个最优子乐曲中,音符\(i\)的后继音符只有两种可能:\(a[p]-a[i]\leqk\)中距离\(i\)最近的\(p\),或者\(a[i]-a[q]\leqk\)中距离\(i\)最近的\(q......
  • 2024 Aug
    ABC366[ProblemA]Election2有\(N\)个人投票选举,两位候选人Takahashi与Aoki分别获得\(T\)票与\(A\)票,请问此时能否确定谁将赢得选举?\(0\leqT,A,T+A\leqN\leq99\),且\(N\)为奇数。设\(M=\dfrac{N+1}{2}\),则判断\(T,A\)是否有一个大于等于\(M\)即可。[......
  • [考试记录] 2024.8.10 csp-s 模拟赛18
    80+20+0+70=170第三题应该有10分暴力的,但我没打。T1星际旅行题面翻译总共有n个节点,m条路径,要求其中m-2条路径走两遍,剩下2条路径仅走一遍,问不同的路径总数有多少,如果仅走一遍的两条边不同则将这两条路径视为不同。样例#1样例输入#15412131415样例输......
  • 腾讯地图SDK Android版开发 3 地图图层
    腾讯地图SDKAndroid版开发3地图图层前言腾讯地图图层地图底图类型地图类图层类型常量接口路况图层接口示例代码地图风格类地图底图类型实时路况页面布局控件响应事件地图底图类型实时路况运行效果图前言本文主要介绍腾讯地图图层相关的功能和接口,以及使用方......