1008题目链接

题意:给定一张n*m的方格,起点在左上角,只能向下或向右走,地图中有k个地雷,人不能走到地雷上,问可能到达的点有多少。

我们考虑行的转移,在这一行上初始不可达点是输入中的点,对于这样的点,它会阻挡人从左方走过来,对于他右侧的点,只有从上方可达转移下来的点才能到,如果这个点右侧的某个点x上方也是不可达的,那么这个点x一定也不可达。也就是说初始不可达点可以向右“染色”,就是找到右方最远的pos,使得pos上方为1也就是不可达。之后将这一段全部染为1.

染色操作和找操作应当都可以使用数据结构优化成log,至于答案就是每一行1的个数,区间查询就行。空间受限肯定不可能开O(n)棵树,显然当前这一行答案只与上一行答案有关,所以我们开两颗树,类似01背包的空间优化那样滚动着用应该就可以了。

线段树模板

#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#define maxn 10005
using namespace std;
int n = 0;
int tree[maxn*4] = { 0 }, arr[maxn] = { 1,2,3,4,5 }, mark[maxn*4] = {0};
void build(int start, int end, int node)
{if (start == end)tree[node] = arr[start];else{int mid = (start + end) / 2;build(start, mid, node * 2);build(mid + 1, end, node * 2 + 1);tree[node] = tree[node * 2] + tree[node * 2 + 1];}
}
inline void pushdown(int node, int start,int end)
{int mid = (start + end) / 2;mark[node * 2] += mark[node];mark[node * 2 + 1] += mark[node];tree[node * 2] += mark[node] * (mid - start + 1);tree[node * 2 + 1] +=mark[node] * (end - mid);mark[node] = 0;
}
void update(int L, int R,int val, int node = 1,int start=0, int end=n-1)
{if (start>R || end<L)return;else{if (L <= start && R >= end){tree[node] += val*(end - start + 1);if (end > start)mark[node] += val;}else{int mid = (start + end) / 2;pushdown(node, start, end);update(L, R, val, node*2,start, mid);update(L, R, val, node*2+1,mid + 1, end);tree[node] = tree[node * 2] + tree[node * 2 + 1];}}
}
int query(int start, int end, int node,int L, int R)
{if (end < L || R < start)return 0;else{if (start >= L && end <= R)return tree[node];else{int mid = (start + end) / 2;pushdown(node, start, end);return query(start, mid, node * 2, L, R) + query(mid + 1, end, node * 2 + 1, L, R);}}
}
int main()
{return 0;
}

此题需要两棵线段树,即tr[2][N<<2]

完整代码:
/*
题意:给定一张n*m的方格,起点在左上角,只能向下或向右走,地图中有k个地雷,人不能走到地雷上,问可能到达的点有多少。
分析:
我们考虑行的转移,在这一行上初始不可达点是输入中的点,对于这样的点,它会阻挡人从左方走过来,对于他右侧的点,只有从上方可达转移下来的点才能到,如果这个点右侧的某个点x上方也是不可达的,那么这个点x一定也不可达。
也就是说初始不可达点可以向右“染色”,就是找到右方最远的pos,使得pos上方为1也就是不可达。之后将这一段全部染为1.
染色操作和找操作应当都可以使用数据结构优化成log,至于答案就是每一行1的个数,区间查询就行。
空间受限肯定不可能开O(n)棵树,显然当前这一行答案只与上一行答案有关,所以我们开两颗树,类似01背包的空间优化那样滚动着用应该就可以了。
*/
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
#define ls (x<<1)
#define rs (x<<1|1)
const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f;
vector<int>e[N];
//lz为懒标记,tr[2][N<<4]:线段树一般size为节点个数的4倍,tr[2]是因为这里用了两棵线段树进行类似滚动数组处理。
int tr[2][N << 2], lz[2][N << 2]; void push_down(int f, int x, int l, int r, int mid)
{if (lz[f][x] == -1)return;tr[f][ls] = lz[f][x] * (mid - l + 1);tr[f][rs] = lz[f][x] * (r - mid);lz[f][ls] = lz[f][rs] = lz[f][x];lz[f][x] = -1;
}
/*
f:哪棵树,tr数组的下标,即tr[f]
x:根节点
l、r:x表示的节点的区间
L、R:需要修改的区间
v:修改的值
*/
void update(int f, int x, int l, int r, int L, int R, int v)
{if (L <= l && R >= r){tr[f][x] = (r - l + 1) * v;lz[f][x] = v;return;}int mid = (l + r) >> 1;push_down(f, x, l, r, mid);if (R <= mid)update(f, ls, l, mid, L, R, v);else if (L > mid)update(f, rs, mid + 1, r, L, R, v);else {update(f, ls, l, mid, L, mid, v);update(f, rs, mid + 1, r, mid + 1, R, v);}tr[f][x] = tr[f][ls] + tr[f][rs];
}
/*
f:哪棵树,tr数组的下标,即tr[f]
x:根节点
l、r:x表示的节点的区间
L、R:需要查询的区间
返回值:左端点位置
*/
int query(int f, int x, int l, int r, int L, int R)
{  if (!tr[f][x])return inf;if (l == r)return l;int mid = l + r >> 1;push_down(f, x, l, r, mid);if (L <= l && R >= r) {if (tr[f][ls] > 0) return query(f, ls, l, mid, l, mid);else return query(f, rs, mid + 1, r, mid + 1, r);}else{if (R <= mid)return query(f, ls, l, mid, L, R);else if (L > mid)return query(f, rs, mid + 1, r, L, R);else return min(query(f, ls, l, mid, L, mid), query(f, rs, mid + 1, r, mid + 1, R));}
}
int main()
{int T;scanf("%d", &T);while (T--) {int n, m, k;scanf("%d %d %d", &n, &m, &k);for (int i = 1; i <= n; ++i)e[i].clear();for (int i = 1; i <= (m << 2); ++i) {tr[0][i] = tr[1][i] = 0;lz[0][i] = lz[1][i] = -1;}for (int i = 0; i < k; ++i) {int x, y;scanf("%d %d", &x, &y);e[x].push_back(y);}long long ans = 0;update(0, 1, 1, m, 1, 1, 1);for (int x = 1; x <= n; ++x) {int l = 0;sort(e[x].begin(), e[x].end());for (auto& y : e[x]){if (y - 1 >= l + 1) {//因为x取值1-n,tr数组下标只有0,1,所以x&1只取0,1。因为a^1=~a,所以(x&1)^1==~(x&1)//tr[(x&1)^1]为保存上一行状态的线段树,tr[(x&1)]为保存当前行状态的线段树,//以下两行代码含义为:查询上一行从1到y-1是否无地雷区间的最左端点pos,并在另一棵树中将[pos,y-1]的区间更新为1。int pos = query((x & 1) ^ 1, 1, 1, m, l + 1, y - 1); if (pos != inf)update(x & 1, 1, 1, m, pos, y - 1, 1);}//l值更新为上一个地雷的位置l = y;}//对最右边的地雷到最右边一列区间的查询和更新if (l + 1 <= m) {int pos = query((x & 1) ^ 1, 1, 1, m, l + 1, m);if (pos != inf)update(x & 1, 1, 1, m, pos, m, 1);}//从保存当前行的线段树的根节点读取可到达的点,保存答案ans += tr[x & 1][1];//将另一个树置0,为下一次循环做准备update((x & 1) ^ 1, 1, 1, m, 1, m, 0);}printf("%lld\n", ans);}return 0;
}

1004题目链接

参考博客

关于后缀数组部分:参考博客

题目分析:二分答案,然后使用任意一种后缀数据结构 check 即可。
如何 check?
后缀数组:所有后缀的所有前缀即所有子串。我们遍历后缀数组,对于每个后缀,其越长的前缀能耗越大,于是可以二分找到能耗小于等于要 check 的值的前缀的个数,再减去重复部分即可。而每个后缀被重复统计的部分就是 height 数组对应的值。
后缀自动机/后缀树:这两种后缀数据结构都是将本质不同的子串记录在其结点上。每个结点表示的子串都是形如Suffix(T,i)+SSuffix(T,i)+SSuffix(T,i)+S的串(即取TTT 的一个后缀SSS连接构成的串,在后缀树上则是再翻转一次的串)。显然我们取的TTT的后缀越长,其表示的子串能耗越大,于是可以二分这个长度来找到满足条件的子串的个数,每次check 对每个结点做一次二分即可。
时间复杂度 O(nlognlogk)O(nlognlogk)O(nlognlogk)

后缀数组

后缀数组,说的简单一点,就是将一个字符串的所有后缀储存起来的数组,接下来分析它的作用。
首先假定一个字符串BANANA,在后面添加一个非字母字符“$”,代表一个没出现过的标识字符,然后把它的所有后缀:BANANA$,ANANA$,NANA$,ANA$,NA$,A$插入到一棵Trie中。由于标识字符的存在,字符串每一个后缀都与一个叶节点一一对应。如图所示:

在绘制后缀Trie的时候,我们将字典序小的字母排在左边。由于叶节点和后缀一一对应,我们现在在每一个叶节点上标上该后缀的首字母在原字符串中的位置,如图:

将所有下标连在一起,构建出来的,就是所谓的后缀数组了。BANANA的后缀数组为sa[] = {5, 3, 1, 0, 4, 2},举个例子,其中sa[1] = 3表示第3 + 1 = 4个字母开头的后缀即"ANA"在所有后缀中字典序排名为1。
后缀数组模板:

#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<string>
#include<queue>
using namespace std;
#define MAXN 1005
#define MAXM 30
char ch[MAXN];
int sa[MAXN], a[MAXN], t[MAXN], c[MAXN], n, m = MAXM, p;
int main()
{scanf("%s", ch), n = strlen(ch);for (int i = 0; i < n; i++) c[a[i] = (ch[i] - 'a' + 1)]++;for (int i = 1; i < m; i++) c[i] += c[i - 1];for (int i = n - 1; i >= 0; i--)sa[--c[a[i]]] = i;for (int k = 1; k <= n; k <<= 1) {int p = 0;for (int i = n - k; i < n; i++) t[p++] = i;for (int i = 0; i < n; i++) if (sa[i] >= k) t[p++] = sa[i] - k;for (int i = 0; i < m; i++) c[i] = 0;for (int i = 0; i < n; i++) c[a[t[i]]]++;for (int i = 0; i < m; i++) c[i] += c[i - 1];for (int i = n - 1; i >= 0; i--) sa[--c[a[t[i]]]] = t[i];swap(a, t);p = 1, a[sa[0]] = 0;for (int i = 1; i < n; i++) a[sa[i]] = (t[sa[i - 1]] == t[sa[i]] && t[sa[i - 1] + k] == t[sa[i] + k]) ? p - 1 : p++;if (p >= n) break;m = p;}return 0;
}

AC代码:

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;
typedef long long ll;const int N = 100010;ll k;
int n, m;
char s[N];
int sa[N], x[N], y[N], c[N], rk[N], height[N];void get_sa()
{for(int i=1;i<=m;i++) c[i] = 0;for (int i = 1; i <= n; i ++ ) c[x[i] = s[i]] ++ ;for (int i = 2; i <= m; i ++ ) c[i] += c[i - 1];for (int i = n; i; i -- ) sa[c[x[i]] -- ] = i;for (int k = 1; k <= n; k <<= 1){int num = 0;for (int i = n - k + 1; i <= n; i ++ ) y[ ++ num] = i;for (int i = 1; i <= n; i ++ )if (sa[i] > k)y[ ++ num] = sa[i] - k;for (int i = 1; i <= m; i ++ ) c[i] = 0;for (int i = 1; i <= n; i ++ ) c[x[i]] ++ ;for (int i = 2; i <= m; i ++ ) c[i] += c[i - 1];for (int i = n; i; i -- ) sa[c[x[y[i]]] -- ] = y[i], y[i] = 0;swap(x, y);//这里用swap要注意N开太大会让代码变慢 x[sa[1]] = 1, num = 1;for (int i = 2; i <= n; i ++ )x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++ num;if (num == n) break;m = num;}
}void get_height()
{for (int i = 1; i <= n; i ++ ) rk[sa[i]] = i;for (int i = 1, k = 0; i <= n; i ++ ){if (rk[i] == 1) continue;if (k) k -- ;int j = sa[rk[i] - 1];while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++ ;height[rk[i]] = k;}
}int w[30],sum[100005];bool check(int now)
{ll res = 0;for(int i=1;i<=n;i++){int pos = upper_bound(sum + 1, sum + 1 + n, now + sum[i - 1]) - sum;int tmp = pos - i;res += tmp - min(tmp, height[rk[i]]);}if(res < k) return 0;return 1;
}int main()
{int T = 1;scanf("%d",&T);while(T--){scanf("%d%lld",&n,&k);scanf("%s", s + 1);for(int i=0;i<26;i++) scanf("%d",&w[i]);for(int i=1;i<=n;i++) sum[i] = sum[i - 1] + w[s[i] - 'a'];m = 122;get_sa();get_height();int l = 0, r = 1e7+7;while(l < r){int mid = (l + r) >> 1;if(check(mid)) r = mid;else l = mid + 1;}if(l < 1e7+7) printf("%d\n",r);else printf("-1\n");}return 0;
}

2021杭电多校补题(4)相关推荐

  1. 2021杭电多校补题——第一场

    2021杭电多校补题--第一场 文章目录 Mod, Or and Everything Rocket land(待补) Puzzle loop(待补) Another thief in a Shop( ...

  2. 2021杭电多校补题(9)

    1. Integers Have Friends 2.0 传送门 题意: 给出一个长度为nnn 的数组,选出一个最长的子序列,存在一个 m≥2m \geq 2m≥2 使得 ak1%m=ak2%m=ak ...

  3. 2021杭电多校补题(6)

    1001题目链接 题意:给定数x,求包含x的区间和为素数的最小区间长度 分析: 显然 r > 0,r ≥ l,如果 l ≤ 0,此时 [l,r] 区间和 = [−l + 1,r] 的区间和,且 ...

  4. 2021杭电多校补题(3)

    1009题目链接 设 fijkf_{ijk}fijk​ 表示从 (1,1) 走到 (i,j),一路上收集了 k 个钻石时,钻石的单价最高能涨到多少,则 ans = max(k × f n,n,k ). ...

  5. 2021杭电多校补题(1)

    1005 https://acm.hdu.edu.cn/showproblem.php?pid=6954) 思路:边权为lcm(a,b).因为lcm(a,b)=a*b/gcm(a,b),所以有两种情况 ...

  6. 2021杭电多校补题(5)

    传送门 1. Array 题意: 给出一个长度为 nnn 的数组,求其有多少个区间满足其众数个数大于区间长度的一半. 题解: 考虑分治. 对于一个区间(l,r)(l,r)(l,r),其中点为 midm ...

  7. 2021杭电多校补题(2)

    1. I love max and multiply 题意: 给出两个数组aaa和bbb ,定义ccc数组 ck=max(ai⋅bj)c_k =max(a_i \cdot b_j)ck​=max(ai ...

  8. 2021杭电多校第八场补题

    比赛传送门:Contest Problem List (hdu.edu.cn) 1006)GCD Game 题目翻译:爱丽丝和鲍勃正在玩游戏. 他们轮流操作.有n个数字,a1,a2,...,an.每次 ...

  9. 【2021杭电多校赛】2021“MINIEYE杯”中国大学生算法设计超级联赛(10)签到题2题

    Solved Pro.ID Title Ratio(Accepted / Submitted) 1001 Pty loves sequence 25.00%(52/208) 1002 Pty with ...

最新文章

  1. Linux下监控文件系统
  2. 学长毕业日记 :本科毕业论文写成博士论文的神操作20170328
  3. 工作和人工智能的未来
  4. 《计算机应用》实践考核,《管理系统中计算机应用》实践性环节考核方案
  5. 解决 html5 input type='number' 类型可以输入e
  6. R语言-异常数据处理3
  7. python语言检测模块langid、langdetect使用
  8. 渝粤题库 陕西师范大学 《文字学概论》作业
  9. MATLAB卷积运算(conv)以及通用的卷积函数my_conv的实现
  10. w讠ndows Boot Manager,开机出现windows boot manager的解决方法和步骤(图文教程)
  11. 【课程作业|图论】第四章课后习题
  12. JAVA中数组和集合的区别
  13. 浏览器快捷键大全、常用快捷键整理
  14. 如何看误差累积分布图
  15. 序列试题---最大子序列、最长递增子序列、最长公共子串、最长公共子序列、字符串编辑距离 .
  16. 正大期货市场基础知识
  17. OSI与TCP/IP的协议
  18. 入职大半年 -- 工作分享
  19. 猫狗大战pytorch实现
  20. 示波器基本使用教程-冻结显示与余辉显示的区别

热门文章

  1. 输入'niHao aAz'实现输出为'ni_hao a_az'
  2. (三十五)相关变量与期权价格的关系、希腊值
  3. XTU OJ 1338 Sandglass打个图图
  4. Ubuntu 16.04 安装 TeamViewer
  5. 超2TB缓存 Radware进军中国云安全市场
  6. Word插入的分节符(下一页)自动变为分节符(连续)的解决办法
  7. linux上查找输入过得命令,linux快速搜索已经输入的命令:
  8. 华为OD机试 - 幻方修复(Java JS Python)
  9. 做百度钱包相关调查问卷有感
  10. 课表APP(未完成版)