未完待续
第一场:
主要有三道 思维题 得到经验 对于看上去很复杂的题目 其实 在某种 程度上很可能就是 关于思维或者是数学方面的找规律题目 应该耐下心来 慢慢找
再就是 一到对于超时问题的优化 可以用set代替 数组的遍历 从而节省 时间 以及lower_bound 和 upper_bound 两种函数 在set中寻找目标的值写法 不只可以单单应用在数组中 还有就是 通过这道题目 也可以用 优先队列的方法进行解答 应该给予相应的重视与学习
下面要学习的相关算法:
1.数据的离散化 这个已经用到很多了 学习一下
2.最短路和最小生成树算法的学习 主要最短路再学习flyod算法和最小生成树再了解 迪杰斯特拉算法 加上 优先队列的 的优化算法
3.线段树对于最小子区间和的维护 以及要复习 线段树其他的相关操作
补题任务
F J L先完成这三道
F:
cnt大小的不断变化 代表着不同类型的消息 他们数的大小也就是他们产生的先后顺序 每一个数就代表着是一个消息
对应着三种操作
1:编号为x的应用生成一条未读消息
2:吧编号为x的应用所有的消息标记为已读 不管是否已经有读过的消息
3:把前t条消息标记为已读
进行完每一次操作后 输出当前手机上所有应用 未读的消息总数是多少
用 vector 和 set 这两个stl函数 来完成
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 3e5 + 7;
// 要学会 用 vector数组 来存放有关系 两个量 一个应用 可能生成不同的消息因此用 vector搞定
vector<int>app[N];// 用来记录某个应用 生成的 消息总数
set<int>num;// 数量
int n,q,x,t,cnt;// 这里用 cnt一变化 一个数便代表着一种类型的数
int main()
{
while(scanf("%d%d",&n,&q) != EOF)
{
cnt = 0;// 也代表着消息的类型
num.clear();
int pos = 0;
//scanf("%d%d",&t,&x);
for(int i = 1;i <= q;i ++)
{
scanf("%d%d",&t,&x);
if(t == 1)
{
app[x].push_back(++cnt);
num.insert(cnt);// 这集合代表的是 消息的个数
}
else if(t == 2)
{
for(int i = 0;i < app[x].size();i ++)
{
num.erase(app[x][i]);// 把这些元素从集合从集合中删去
}
app[x].clear();// 向量数组直接清空即可
}
else
{
for(int i = pos;i <= x;i ++)
{
num.erase(i);
}
pos = max(pos,x);// 这个操作可避免 如果第一次 删了前五条 下一次要删前三条的话 就 不用再删了 避免了重复操作
}
printf("%d\n",num.size());
}
}
return 0;
}
J:经典 二维前缀和 差分
先确定一下什么是二维前缀和
一维前缀和就是 给定区间【L,R】求解区间中的元素的和是多少 只需要用数组 这么存数据即可
a[i] += a[i-1];这样数组记录的就是 以给定数值为结尾的 一段数据的和
ans = a[r] - a[l-1];即可实现
自然的二维前缀和 也可以如此的表示
int mp[17][17];
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= m;j++)
{
scanf("%d",&mp[i][j]);
mp[i][j] += mp[i][j-1] + mp[i-1][j] - mp[i-1][j-1];// 这就是 求解代码 也就是求解矩形的面积的大小的代码
}
int ans = 0;
scanf("%d%d%d%d",&x1&y1&x2&y2);
ans = mp[x2][y2] - mp[x2-1][y2] - mp[x2][y2-1] + mp[x1][y];//求解二维前缀和的代码
}
差分: 是对于区间进行一个操作
然后在查询一个区间的和为多少
是基于 前缀和的基础上 在增肌一个数组 来实现的
对于一维的差分
int main()
{
int a[107];int b[107];
// 一维的差分
// 如果对于每次操作 我都 把 区间内的每个元素 加上一个数值 那么 肯定会超市 所以
// 引入另一个数组 b
int l,r,q,t;
scanf("%d%d%d%d",&l,&r,&t,&q);
if(t == 1)// 代表着 对区间进行 加上q的操作
{
b[l] += q;
b[r+1] -= q;// 这里为什么要减去q 因为我们求的是 前缀和 后面的数会加上前面的加上的q
// 但实际上 我们只对 l到r区间内的数加上了q 而 r之后的数如果不减去 那么最后也会加上一个q
}
else if(t == 2)
{
b[l] -= q;
b[r+1] += q;// 与加同理
}
int add = 0;
for(int i = 1;i <= n;i ++)
{
add += b[i];// 因为有多次操作 要把每次操作的累积下来 加到后面去
a[i] += a[i-1] + add;
}
return 0;
}
二维的差分:
int main()
{
int mp[107][107];int b[107][107];
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= m;j ++)
{
scanf("%d",mp[i][j]);
mp[i][j] += mp[i][j-1] + mp[i-1][j] - mp[i-1][j-1];
// 二维前缀和
}
}
int x1,x2,y1,y2,q;
scanf("%d%d%d%d%d",&x1,&x2,&y1,&y2,&q);
// 二维差分
// 把这个区间内的所有值 加上q
b[x1][y1] += q;
b[x1][y2 + 1] -= q;
b[x2 + 1][y1] -= q;
b[x2 + 1][y2 + 1] += q;
return 0;
}
https://www.cnblogs.com/LMCC1108/p/10753451.html// 这篇讲解 配上画图 很清晰
J: 二维前缀和 + 差分
二维的差分
HDU 6514 经典的 二维前缀和 加 差分 算法
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,m,p,q;
while(scanf("%d%d",&n,&m) != EOF)
{
int b[n+5][m+5];
int mp[n+5][m+5];
memset(b,0,sizeof(b));
memset(mp,0,sizeof(mp));
scanf("%d",&p);
int x1,x2,y1,y2;
while(p--)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
b[x1][y1]++;
b[x2+1][y1]--;
b[x1][y2+1]--;
b[x2+1][y2+1]++;
}
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= m;j ++)
{
b[i][j] += b[i][j-1] + b[i-1][j] - b[i-1][j-1];
}
}
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= m;j ++)
{
if(b[i][j])
b[i][j] = 1;// 因为 差分数组 可能会在一个地方 操作多次 但是 我最后要把它变成1 所以 要加上这一步 再来构造 监控好的地方
}
}
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= m;j ++)
{
mp[i][j] = b[i][j] + mp[i-1][j] + mp[i][j-1] - mp[i-1][j-1];
}
}
scanf("%d",&q);
while(q--)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
int ans = mp[x2][y2] - mp[x2][y1-1] - mp[x1-1][y2] + mp[x1-1][y1-1];
if(ans == (x2-x1+1) * (y2-y1+1))
printf("YES\n");
else
printf("NO\n");
}
}
return 0;
}