真·奥义·随意的hu测(18.2.28)

64 篇文章 0 订阅
61 篇文章 0 订阅

今天的hu测真心随意,也没有好好写代码

T1,T3真心难
大家就当看着玩吧

T1

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

题解:
对于一个良好数列,我们考虑按数值从小到大加入数列中的元素

若当前加入的元素中最大值为x,若数列中一个区间内的数大于x,则那些位置出现空缺,且空缺两端都是x,我们称这类区域为待补区域

对于一个良好数列,在上述加入元素过程中出现过的任一数列,我们称之为准良好数列

由准良好数列从小到大排序后形成的数列,我们称之为准优秀数列

我们按照上面所说的插入数的过程进行动态规划
定义f[i][j][p][q],表示当前数列中最大值为i,长度为j,待补区域数为p,通过重排可形成q个准良好数列的准优秀数列个数

可以用滚动数组优化空间

#include<cstdio>
#define ll long long

using namespace std;

const int N=1000005;
const int p=1e9+7;
int dp[2][102][102][102];
bool vis[2][102][102][102];
int q[2][N][3],wei[2];
int n,m,K,g[102][102];
int ans=0;

int main() {
    int i,j,k,l,s,t;
    scanf("%d%d%d",&n,&m,&K);

    g[0][0]=1;
    for (j=1;j<=n;j++)
        for (i=0;i<=n;i++)
            for (k=0;k<=i;k++) 
            {
                g[i][j]+=g[k][j-1];
                if (g[i][j]>=p) g[i][j]-=p;
            }

    for (i=1;i<=n;i++) 
    {
        wei[0]++;
        q[0][wei[0]][0]=i;
        q[0][wei[0]][1]=i;
        q[0][wei[0]][2]=1;
        dp[0][i][i][1]=1;
    }

    for (i=0;i<m;i++) 
    {
        int o=(i&1);
        int now=o^1;
        for (s=1;s<=wei[now];s++)
            vis[now][q[now][s][0]][q[now][s][1]][q[now][s][2]]=0;
        wei[now]=0;
        for (s=1;s<=wei[o];s++) 
        {
            j=q[o][s][0];
            k=q[o][s][1];
            l=q[o][s][2];
            if (k) 
            {
                for (t=0;j+k+t<=n && l*g[t][k]<=K;t++) 
                {
                    if (!vis[now][j+k+t][t][l*g[t][k]])
                    {
                        vis[now][j+k+t][t][l*g[t][k]]=1;
                        dp[now][j+k+t][t][l*g[t][k]]=0;
                        wei[now]++;
                        q[now][wei[now]][0]=j+k+t;
                        q[now][wei[now]][1]=t;
                        q[now][wei[now]][2]=l*g[t][k];
                    }
                    dp[now][j+k+t][t][l*g[t][k]]+=dp[o][j][k][l];
                    if (dp[now][j+k+t][t][l*g[t][k]]>=p)
                        dp[now][j+k+t][t][l*g[t][k]]-=p;
                }
            }
            else
            {
                ans+=(((ll)(m-i)*dp[o][j][k][l])%p);
                if (ans>p) ans-=p;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

T2

T3

这里写图片描述

这里写图片描述
这里写图片描述

题解:
先将坐标离散化。
直接求有多少矩形与(x1,y1,x2,y2)有公共点比较难实现,可以用总矩形数
减去与(x1,y1,x2,y2)没有公共点的矩形(a,b,c,d)的数量

  • 若 c<x1,没有公共点
  • 若 d<y1,没有公共点
  • 若 a>x2,没有公共点
  • 若 b>y2,没有公共点

这些数量都可以用线段树来维护
但有些与(x1,y1,x2,y2)没有公共点的矩形被计算了两次

  • c<x1,且 d<y1
  • c<x1,且 b>y2
  • a>x2,且 d<y1
  • a>x2,且 b>y2

维护每一个值都相当于是要支持以下两种操作:

  • 加入一个点
  • 求一个矩形范围内点的个数

这些操作可以用树状数组套线段树实现,但会超出内存限制

那么可以用分块的思想:
先对每个横坐标建立一棵线段树记录横坐标等于这个值的点的纵坐标有哪些,对横坐标分块,
再对每个块建立一棵线段树记录横坐标在这个块范围内的点的纵坐标有哪些
修改就修改横坐标的线段树和对应块的线段树
查询时整块的部分在块的线段树上查询,其他部分在横坐标的线段树上查询

这样的时间复杂度是O(m sqrt(m) log(m)) ,但还是有可能会超出时间限制

既然可以分一次块,那就可以继续分,对已经分出的那些块再进行一次分块,
同样对每一个由块组成的大块也建立一棵线段树
修改时要修改大块的线段树、小块的线段树、横坐标的线段树
查询时先找完整的大块,再在其他部分找完整的小块,剩下的部分就每一个横坐标单独计算

这样就可以通过所有测试数据了

//std

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
const int N = 100500;
const int MN = 200500;
const int M = 18005000;
int m;
struct Rec
{
    int a,b,c,d;
};
struct Opt
{
    Rec rec;
    int opt;
}xu[N];
int insc[N];
int xux[MN],xuy[MN];
int size;
struct Node
{
    Node *lson,*rson;
    int val;
}tree[M];
int cc;
struct Tree
{
    Node *point[MN];
    Node *lumpx[MN];
    Node *lump[MN];
}zs,ys,zx,yx;
char getopt()
{
    char ch=getchar();
    while(ch<'A' || ch>'Z')
        ch=getchar();
    return ch;
}
int getint()
{
    int res=0;
    char ch=getchar();
    while((ch<'0' || ch>'9') && ch!='-')
        ch=getchar();
    bool fan=0;
    if(ch=='-')
    {
        fan=1;
        ch=getchar();
    }
    while('0'<=ch && ch<='9')
    {
        res=res*10+ch-'0';
        ch=getchar();
    }
    if(fan)
        res=-res;
    return res;
}
void getrec(Rec &x)
{
    x.a=getint();
    x.b=getint();
    x.c=getint();
    x.d=getint();
}
void Discr(int x[])
{
    sort(x+1,x+x[0]+1);
    int t=0,i;
    for(i=1;i<=x[0];i++)
    {
        x[++t]=x[i];
        while(i<x[0] && x[i]==x[i+1])
            i++;
    }
    x[0]=t;
}
int NoLess(int x[],int l,int r,int z)
{
    if(l==r)
        return l;
    int mid=(l+r)/2;
    if(x[mid]>=z)
        return NoLess(x,l,mid,z);
    return NoLess(x,mid+1,r,z);
}
int NoMore(int x[],int l,int r,int z)
{
    if(l==r)
        return l;
    int mid=(l+r+1)/2;
    if(x[mid]<=z)
        return NoMore(x,mid,r,z);
    return NoMore(x,l,mid-1,z);
}
void Discrec(Rec &x)
{
    x.a=NoLess(xux,1,xux[0]+1,x.a);
    x.b=NoLess(xuy,1,xuy[0]+1,x.b);
    x.c=NoMore(xux,0,xux[0],x.c);
    x.d=NoMore(xuy,0,xuy[0],x.d);
}
void GetData()
{
    int i;
    m=getint();
    for(i=1;i<=m;i++)
    {
        char ch=getopt();
        if(ch=='I')
        {
            xu[i].opt=1;
            getrec(xu[i].rec);
            xux[++xux[0]]=xu[i].rec.a;
            xux[++xux[0]]=xu[i].rec.c;
            xuy[++xuy[0]]=xu[i].rec.b;
            xuy[++xuy[0]]=xu[i].rec.d;
            insc[++insc[0]]=i;
        }
        else if(ch=='D')
        {
            xu[i].opt=2;
            xu[i].rec=xu[insc[getint()]].rec;
        }
        else
        {
            xu[i].opt=3;
            getrec(xu[i].rec);
        }
    }
    Discr(xux);
    Discr(xuy);
    for(i=1;i<=m;i++)
        Discrec(xu[i].rec);
    for(size=1;size*size*size<=xux[0];size++);
}
Node *New()
{
    cc++;
    return tree+cc;
}
void Change(Tree &t,int x,int y,int v)
{
    int l,r;
    Node *now;
    if(!t.point[x])
        t.point[x]=New();
    now=t.point[x];
    now->val+=v;
    l=1;r=xuy[0];
    while(l<r)
    {
        int mid=(l+r)/2;
        if(y<=mid)
        {
            if(!now->lson)
                now->lson=New();
            now=now->lson;
            r=mid;
        }
        else
        {
            if(!now->rson)
                now->rson=New();
            now=now->rson;
            l=mid+1;
        }
        now->val+=v;
    }
    x/=size;
    if(!t.lumpx[x])
        t.lumpx[x]=New();
    now=t.lumpx[x];
    now->val+=v;
    l=1;r=xuy[0];
    while(l<r)
    {
        int mid=(l+r)/2;
        if(y<=mid)
        {
            if(!now->lson)
                now->lson=New();
            now=now->lson;
            r=mid;
        }
        else
        {
            if(!now->rson)
                now->rson=New();
            now=now->rson;
            l=mid+1;
        }
        now->val+=v;
    }
    x/=size;
    if(!t.lump[x])
        t.lump[x]=New();
    now=t.lump[x];
    now->val+=v;
    l=1;r=xuy[0];
    while(l<r)
    {
        int mid=(l+r)/2;
        if(y<=mid)
        {
            if(!now->lson)
                now->lson=New();
            now=now->lson;
            r=mid;
        }
        else
        {
            if(!now->rson)
                now->rson=New();
            now=now->rson;
            l=mid+1;
        }
        now->val+=v;
    }
}
void AddRec(Rec &x)
{
    Change(zs,x.a,x.b,1);
    Change(zx,x.c,x.b,1);
    Change(ys,x.a,x.d,1);
    Change(yx,x.c,x.d,1);
}
void DelRec(Rec &x)
{
    Change(zs,x.a,x.b,-1);
    Change(zx,x.c,x.b,-1);
    Change(ys,x.a,x.d,-1);
    Change(yx,x.c,x.d,-1);
}
int FindX(Node *x,int zl,int zr)
{
    if(zr==xuy[0])
    {
        if(x)
            return x->val;
        return 0;
    }
    zr++;   
    int res=0;
    int l=1,r=xuy[0];
    while(l<r && x)
    {
        int mid=(l+r)/2;
        if(zr<=mid)
        {
            x=x->lson;
            r=mid;
        }   
        else
        {
            if(x->lson)
                res+=x->lson->val;
            x=x->rson;
            l=mid+1;
        }   
    }
    return res; 
}
int Find(Tree &t,int a,int b,int c,int d)
{
    if(a>b || c>d)
        return 0;
    int ax=a/size;
    int bx=b/size;
    int axx=ax/size;
    int bxx=bx/size;
    int i;
    int res=0;
    if(bxx-axx>1)
    {
        for(i=axx+1;i<bxx;i++)
            res+=FindX(t.lump[i],c,d);
        for(i=ax+1;i/size==axx;i++)
            res+=FindX(t.lumpx[i],c,d);
        for(i=a;i/size==ax;i++)
            res+=FindX(t.point[i],c,d);
        for(i=bx-1;i/size==bxx;i--)
            res+=FindX(t.lumpx[i],c,d);
        for(i=b;i/size==bx;i--)
            res+=FindX(t.point[i],c,d);
    }
    else
    {
        if(bx-ax>1)
        {
            for(i=ax+1;i<bx;i++)
                res+=FindX(t.lumpx[i],c,d);
            for(i=a;i/size==ax;i++)
                res+=FindX(t.point[i],c,d);
            for(i=b;i/size==bx;i--)
                res+=FindX(t.point[i],c,d);
        }
        else
        {
            for(i=a;i<=b;i++)
                res+=FindX(t.point[i],c,d);
        }
    }
    return res;
}
void FindRec(Rec &x)
{
    int res=0;
    res+=Find(zs,1,x.c,1,x.d);
    res-=Find(zx,1,x.a-1,1,x.d);
    res-=Find(yx,x.a,xux[0],1,x.b-1);
    res+=Find(ys,x.c+1,xux[0],1,x.b-1);
    printf("%d\n",res);
}
void DoIt()
{
    int i;
    for(i=1;i<=m;i++)
    {
        if(xu[i].opt==1)
            AddRec(xu[i].rec);
        else if(xu[i].opt==2)
            DelRec(xu[i].rec);
        else
            FindRec(xu[i].rec);
    }
}
int main()
{
    freopen("rec.in","r",stdin);
    freopen("rec.out","w",stdout);
    GetData();
    DoIt();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值