2019暑假集训8.24(problem2.baritone)(链表(巧妙数据结构))

数据结构好题!

因为思路是第一次见,所以就直接说思路。

题目抽象:

这是一个矩形

里面有很多的点,求至少覆盖k个点的矩形有多少个

先确定上边界,下边界为低端

上边界下面的点用链表存起来

考虑以每个点作为左边界的贡献(线上的点也算在矩形内),假如k=3,那么右边界至少在橙色这根线这儿

符合要求的矩形的左边界范围L和右边界范围R如图

做出的贡献为L*R,如果前面还有点注意是左开右闭(因为右边到底都是可以的)

然后移动左边界

到下一个点再计算贡献

再来移动下边界

一些点可能在同一水平线上,所以一开始可以用vector存起来,然后要删的点一个一个处理。现在考虑下边界移上去后删去一些点的影响

比如删去这个点

(k=3),对于前面k-1个点和后面一个点会造成影响:前面k-1个点:删去后因为每次至少覆盖k个点,所以右边界要向右移动一个点;后面一个点:因为删去了前面的那个点,所以左边界要向左移动



所以总的思路就是:枚举上边界,确定左边界和右边界,再移动下边界。

时间复杂度O(n^2 k)。上边界往下,下边界走到上边界,上边界n条,相当于图从上往下用线扫n次,每次链表中最多n个点,那么左边界最多有n条,每一条往后推k个点,n*n*k=n^2*k。

实现的细节在代码里有说,记得注意左开右闭以及删点影响的范围(画图看一下就明白了)。

#include<bits/stdc++.h>
#define N 3021
#define LL long long
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<‘0‘||s>‘9‘){if(s==‘-‘)f=-1;s=getchar();}
    while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();}
    return x*f;
}
vector<int>belong[N];
struct point{
    int x,y;
}a[N*N];
LL val[N],id[N],pointx[N],pointy[N],nextt[N],pre[N],ord[N];
LL sum=0,ans=0,cnt=0,k,r,c,n;
bool cmp(const int &u,const int &v)
{
    if(pointy[u]==pointy[v])return pointx[u]<pointx[v];
    return pointy[u]<pointy[v];
}
void del(int d)
{
    nextt[pre[d]]=nextt[d];
    pre[nextt[d]]=pre[d];
    sum-=val[d];//以d为左边界的矩形要减去
    int x=nextt[d],y=nextt[d];//对前k的点和后一个点作为左边界会有影响
    for(int i=1;i<k;++i)y=nextt[y];
    for(int i=1;i<=k;++i)
    {
        int v=(pointy[x]-pointy[pre[x]])*(c-pointy[y]+1);//如果说两点在同一竖直线上则v=0,也满足要求
        sum+=v-val[x];
        val[x]=v;
        x=pre[x];y=pre[y];
    }
}
int idc;
int main()
{
    freopen("baritone.in","r",stdin);
    freopen("baritone.out","w",stdout);
    r=read(),c=read(),n=read();k=read();
    for(int i=1;i<=n;++i)
      a[i].x=read(),a[i].y=read(),belong[a[i].x].push_back(i);////保存这一行有那些点
    for(int i=1;i<=r;++i)//枚举上边界
    {
        sum=0,cnt=0;//sum是全局变量,每一层下边界改变时会变化,每一个上边界对应一个sum
        for(int j=0;j<=10;++j)//防止越界,k最大是10
          pointy[++cnt]=0,pointy[++cnt]=c+1;
        for(int j=1;j<=n;++j)
          if(a[j].x>=i)//pointx 行坐标,pointy 纵坐标
            pointy[++cnt]=a[j].y,pointx[cnt]=a[j].x,ord[j]=cnt;//ord[j]是第j个点在链表中的序号
        for(int j=1;j<=cnt;++j)id[j]=j;
        sort(id+1,id+1+cnt,cmp);
        for(int j=1;j<cnt;++j)//前驱后继
          nextt[id[j]]=id[j+1],pre[id[j+1]]=id[j];
        nextt[id[cnt]]=id[cnt];pre[id[1]]=id[1];
        for(int j=1;j<=cnt;++j)//枚举左边界
        {
            int R=j;
            for(int p=1;p<k;++p) R=nextt[R];//R为右边界
            val[j]=(pointy[j]-pointy[pre[j]])*(c-pointy[R]+1);//乘法原理 ,右边界一直到c都是满足的
            sum+=val[j];//val上边界为i,下边界为r,左边界为j的矩形个数
        }
        for(int j=r;j>=i;--j)//枚举下边界
        {
            ans+=sum;
            for(int p=0;p<(int)belong[j].size();++p)
              del(ord[belong[j][p]]);
        }
    }
    printf("%lld\n",ans);
}
/*
2 2 1 1
1 2

3 2 3 3
1 1
3 1
2 2

3 2 3 2
1 1
3 1
2 2
*/

这道题还是很有价值的!??ヽ(°▽°)ノ?

原文地址:https://www.cnblogs.com/yyys-/p/11405741.html

时间: 2024-10-03 22:55:59

2019暑假集训8.24(problem2.baritone)(链表(巧妙数据结构))的相关文章

「总结」2019暑假集训

啊,我最喜欢的暑假集训终究还是结束了. 感觉集训收获的还是挺大的,不管是在知识方面还是心态方面,感觉现在考试心态稳了很多,不管是考前考时考后,都可以很快的调整了.大概就是教练所说的考试心态调整的加速.最近感觉非常好,虽然水题还是老爆零,考得也不怎么样,不过我的确是飞快的在进步了,只要我在进步就好了,我很心满意足的. 总结一下接近30场的考试吧. 一开始的7场一直只有50分左右,而且还有两次没有交卷子,气得我把纸贴在电脑上提醒自己交卷子,虽然成绩并不怎么样,不过我还好在也不犯这个错误了. 然后就理

2019暑假集训8.22(problem2.dinner)(二分)

因为求最大时间的最小,考虑到二分答案. 常规思路:断环为链,二倍链. 最暴力的做法是在n个位置都断一次环,序列for一遍暴力分组,大于mid了就分到下一组,时间O(n^2 logn) 考虑优化: 我们发现每一次暴力分组是把整个序列都给走了一遍,分好的组满足和<=mid,与其一个个的把值加到和里,不如先处理出前缀和,然后对于每一个组的起点,二分找出这个组的终点(我代码中是用upper_bound实现的) int s=t[q],c=1;//s即为起点 int flagg=0; for(int i=q

2019暑假集训DAY1(problem3.play)(单调栈思想)

题面 play 题目大意 这个位面存在编号为1~2N的2N个斗士,他们正为争夺斗士大餐展开R轮PVP,每个斗士i都有一个固有APM ai,和一个初始斗士大餐储量 bi.每轮开始前以及最后一轮结束之后,2N个斗士会重新按照各自斗士大餐的储量进行排序(斗士大餐储量相同时编号小的靠前),每轮中,第1名和第2名PVP,第3名和第4名PVP,……第2k-1名和第2k名PVP,第2N-1名和第2N名PVP.而每场一对一的PVP都非常无聊,总是两个斗士中APM高的获胜,另一方失败:或者APM相同的两方取得平手

2019暑假集训 Intervals

题目描述 给定n个闭区间[ai,bi]和n个整数ci.你需要构造一个整数集合Z,使得对于任意i,Z中满足ai<=x<=bi的x不少于ci个.求Z集合中包含的元素个数的最小值. 输入 第一行为一个整数n(1<=n<=50000) 接下来n行每行描述一个区间,三个整数分别表示ai,bi和ci.( 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1.) 输出 输出一个整数,表示Z中包含元素个数的最小值. 样例输入 5

2019暑假集训 8/2

学习内容:线段树+可持久化线段树 今日完成题数(不包含多校):5 /*多校补题情况(之前定的每支队伍标准):?*/ 今日看书情况:3页 学习算法的总结 可持久化线段树一直没有好好研究 直到最近着重开始写线段树专题 写了一些权值线段树 才把之前有的疑问的解决了 今日做题总结    hdu-6183 https://www.cnblogs.com/MengX/p/11291321.html hdu-4630 https://www.cnblogs.com/MengX/p/11291349.html

2019暑假集训 数字游戏

题目描述 科协里最近很流行数字游戏.某人命名了一种不降数,这种数字必须满足从左到右各位数字成小于等于的关系,如123,446.现在大家决定玩一个游戏,指定一个整数闭区间[a,b],问这个区间内有多少个不降数. 输入 有多组测试数据.每组只含两个数字a,b,意义如题目描述. 输出 每行给出一个测试数据的答案,即[a,b]之间有多少不降数. 样例输入 1 9 1 19 样例输出 9 18 提示 对于全部数据,1<=a<=b<=2^31-1. 裸的数位dp,每一位从上一位开始搜即可(保证不下降

2019暑假集训 8/16

学习内容:网络流 今日完成题数(不包含多校): 今日看书情况:无 学习算法的总结 有上下界的网络流 和 最小费用可行流 今日做题总结 总结 今日心得: https://blog.csdn.net/forever_dreams/article/details/81879277 https://blog.csdn.net/qq_43202683/article/details/90049597 明日任务:   明天写完基础的可行流题目 就可以写一写gym的进阶题 原文地址:https://www.c

2019暑假集训 文件压缩

题目背景 提高文件的压缩率一直是人们追求的目标.近几年有人提出了这样一种算法,它虽然只是单纯地对文件进行重排,本身并不压缩文件,但是经这种算法调整后的文件在大多数情况下都能获得比原来更大的压缩率. 题目描述 该算法具体如下:对一个长度为nn的字符串SS,首先根据它构造nn个字符串,其中第ii个字符串由将SS的前i-1i−1个字符置于末尾得到.然后把这nn个字符串按照首字符从小到大排序,如果两个字符串的首字符相等,则按照它们在SS中的位置从小到大排序.排序后的字符串的尾字符可以组成一个新的字符串S

2019暑假总结7.15-8.23+大一总结

2019暑假集训7.15-8.23 今年大一下学期整个暑假基本上是了解学习了dp.动态规划.线段树.树状数组.贪心.搜索.背包问题.记忆化搜索.欧拉回路.最大流.最短路.最小生成树.二叉搜索树.kmp.数论..马拉车等,感觉每天在敲代码,可是整体看下来学的东西很少,但是谢谢题目又是好久才能写出一道题. 等到明天九月二号一来到,我就是大二的学生了,大二这整一年是最关键的一年,一定要加油加油. 原文地址:https://www.cnblogs.com/OFSHK/p/11444186.html