暑假考试题3:baritone 上低音号与星星(链表+矩形统计)

Posted mowanying

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暑假考试题3:baritone 上低音号与星星(链表+矩形统计)相关的知识,希望对你有一定的参考价值。

 

题目:

技术图片

n,r,c<=3000

分析:先枚举左边界   然后将点从高到矮连链表   再统计从每一个点开始含括k个点的矩形   能够上下延伸得到的多少个矩形。
然后枚举右边界删点    利用大矩形原有的信息修改后    去累加新的左右宽度较小的矩形的贡献。

这种算法的优势: 每一次缩小矩形的时候 可以不用重新计算小矩形的贡献 只需要通过大矩形原有的贡献进行修改
修改方式:通过枚举右边界缩小矩形 删掉超出右边界范围内的那一列的点
删点时重新统计贡献 :也就是对被影响的点重新计算一下(通过跳链表找到受影响的点)

ys的图与详解

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 4005
int zong[N],x[N],y[N],r,c,n,k,q[N],val[N],nex[N],to[N],pre[N],num[N];
ll sum,ans=0;
vector<int>v[N];
bool cmp(int a,int b)

    if(q[a]==q[b]) return zong[a]<zong[b];
    return q[a]<q[b];

void del(int t)

    nex[pre[t]]=nex[t];//通过链表连边的方式删去t点 
    pre[nex[t]]=pre[t];
    sum-=val[t];//删掉这个点 就应该把其贡献去掉 
    int x=nex[t],y=nex[t];//跳nex是因为影响了下面一个点 因为下面一个点的上界是通过t来算的 t变了  
    for(int i=1;i<=k-1;i++) y=nex[y];
    for(int i=1;i<=k+1;i++)
        int v=(q[x]-q[pre[x]])*(r-q[y]+1);
        //这个点原来的val就是这个点向右包含恰好k个点 然后向上下延伸得到的矩形个数 
        sum+=v-val[x];// val贡献也应随着改变 所以应该重新统计一下x这个点的贡献 
        val[x]=v;
        x=pre[x],y=pre[y];//因为t被删去了 所以前面通过t这个点向右延伸达到k个的val值都应该改变 
    

int main()

    freopen("baritone.in", "r", stdin);
    freopen("baritone.out", "w", stdout);
    int len=0;
    scanf("%d%d%d%d",&r,&c,&n,&k);
    for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]),v[y[i]].push_back(i);
    for(int i=1;i<=c;i++)//枚举左边界 
        sum=0; len=0;
        for(int j=0;j<=20;j++)//防止越界 加入40个虚点 使得前面有20个点 后面有20个点 
         q[++len]=0,q[++len]=r+1;
        for(int j=1;j<=n;j++)
        if(y[j]>=i)//如果这个点在枚举的左边界之内 就放入队列 维护相关信息 
            q[++len]=x[j];//q是存的横坐标!! 
            zong[len]=y[j];
            to[j]=len;//to是在链表中的编号 
        
        for(int j=1;j<=len;j++) num[j]=j;//num是每一个点的标号数组 便于对前len个在队列中的点 按横坐标为第一关键字排序 
        sort(num+1,num+1+len,cmp); 
        for(int j=1;j<=len-1;j++)
        nex[num[j]]=num[j+1],pre[num[j+1]]=num[j];//求每一个点的前驱和后继
        //for(int j=1;j<=len;j++) printf("%d ",pre[j]);
        nex[num[len]]=num[len];
        pre[num[1]]=num[1];
        for(int j=1;j<=len;j++)//接下来是答案的统计 
            int now=j;
            for(int p=1;p<=k-1;p++)//跳k-1次恰好跳到的点 使得j和now之间恰好有k个点(包含它们本身) j和now是上下关系 
            now=nex[now];
            val[j]=(q[j]-q[pre[j]])*(r-q[now]+1);
            //从pre[j]到 j表示 j可以向上延伸的长度 从q[now]到r是now向下可以延伸的长度 
            //根据乘法原理累计答案 +1是因为有一方可以不延伸
            sum+=val[j]; 
         //printf("ans:%lld\\n",ans);
        for(int j=c;j>=i;j--)
            ans+=sum;
            for(int p=0;p<v[j].size();p++)//右边界向左移动 删去在这一列的所有点 因为下一次它们就超出范围了
            del(to[v[j][p]]);//找到第j列的所有点在链表中的编号 然后删掉 更新答案 
        
        //当右边界向左边界延伸的时候 其实是为了得到左右宽度更小的矩形答案 
        
    
    printf("%lld\\n",ans);

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

 

以上是关于暑假考试题3:baritone 上低音号与星星(链表+矩形统计)的主要内容,如果未能解决你的问题,请参考以下文章

七中高新 NOIP模拟题 第二题 上低音号 题

TMD模拟赛上低音号 链表

暑假集训结束

暑假NOIP期末考试—— Phantom

暑假考试题2:Nim游戏 改(博弈论)

大暑假集训总结(反思)