首页 > 其他分享 >P4898 [IOI2018] seats 排座位

P4898 [IOI2018] seats 排座位

时间:2024-11-02 14:09:36浏览次数:1  
标签:IOI2018 val int xx 黑点 P4898 && seats zs

题目链接

主要算法:

线段树(虚假的),奇技淫巧(真正的)

思路:

1.初步:考虑如何保证一个区间坐好后是一个矩形,有一个思路从另一个题中启示我们维护 \(xmin,xmax,ymin,ymax\) ,但是这样无法保证在中间挖一个空的情况(有一个别的题解,可以染色后维护四个角和一个判框的东西),但我们觉得就算可以维护这样一次修改会改动很多东西,不太好做。
2.进一步:所以引入一个染色,在第\(i\)的时间将第\(i\)个人染成黑色,这时图变成了黑白两色,所以考虑黑点和白点的性质。对于左上角的黑点它的上下都是白点且其它黑点如果一个矩形都不满足,对于每一个白点如果四联通有两个黑点,就会出现拐角,不是矩形。所以对于矩形,满足条件的黑白点总数是1(一个黑色点+0个白色点),就可以写出不交换时的程序.

int black(int x,int y)
{
    int ans=zs+1;
    for(int i=0;i<=1;i++)
    {
        int xx=x+dx[i],yy=y+dy[i];
        if(xx>=1&&xx<=n&&yy>=1&&yy<=m)ans=min(ans,num(xx,yy));
    }
    return ans;
}//满足黑点要求的最晚时间,左上第一个黑点出现
int white(int x,int y)
{
    int bb[5];
    for(int i=0;i<=3;i++)
    {
        int xx=x+dx[i],yy=y+dy[i];
        if(xx>=1&&xx<=n&&yy>=1&&yy<=m)bb[i]=num(xx,yy);
        else bb[i]=zs+1;
    }
    sort(bb+0,bb+4);
    return bb[1];
}//满足白点要求的最早时间,第二个黑点出现
void js(int h,int l,int i)
{
    val[i]=val[i-1];
    if(white(h,l)<i)val[i]--;
    if(black(h,l)>i)val[i]++;
    for(int j=0;j<=3;j++)
    {
        int x=h+dx[j],y=l+dy[j];
        if(x>=1&&x<=n&&y>=1&&y<=m)
        {
            if(white(x,y)==i&&i<num(x,y))val[i]++;
            if(black(x,y)==i&&i>num(x,y))val[i]--;
        }
    }
}

3.交换:刚才的做法就是因为不好维护交换而被暴毙。所以考虑交换\(u\),\(v\)有什么影响,一定只对四联通有变化。最多改 \(10\) 个点。改这十个点有贡献的时间,区间修改。所以用线段树维护一下即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m,q,zs,kz[N],val[N],st[20];
#define bianhao(x,y) (x-1)*m+y
#define num(x,y) kz[bianhao(x,y)]
int dx[5]={-1,0,1,0},dy[5]={0,-1,0,1};
int black(int x,int y)
{
    int ans=zs+1;
    for(int i=0;i<=1;i++)
    {
        int xx=x+dx[i],yy=y+dy[i];
        if(xx>=1&&xx<=n&&yy>=1&&yy<=m)ans=min(ans,num(xx,yy));
    }
    return ans;
}//满足黑点要求的最晚时间
int white(int x,int y)
{
    int bb[5];
    for(int i=0;i<=3;i++)
    {
        int xx=x+dx[i],yy=y+dy[i];
        if(xx>=1&&xx<=n&&yy>=1&&yy<=m)bb[i]=num(xx,yy);
        else bb[i]=zs+1;
    }
    sort(bb+0,bb+4);
    return bb[1];
}//满足白点要求的最早时间
struct node
{
    int h,l;
}a[N];
struct stu
{
    int mn,cnt,lz;
}sh[N<<2];
void js(int h,int l,int i)
{
    val[i]=val[i-1];
    if(white(h,l)<i)val[i]--;//这个点变黑之前是满足条件的白点 
    if(black(h,l)>i)val[i]++;//这个点变黑之后是满足条件的黑点 
    for(int j=0;j<=3;j++)
    {
        int x=h+dx[j],y=l+dy[j];
        if(x>=1&&x<=n&&y>=1&&y<=m)//一定要判边界 
        {
            if(white(x,y)==i&&i<num(x,y))val[i]++;//它是白点的第二个出现的黑点,所以从现在开始是一个满足条件的白点 
            if(black(x,y)==i&&i>num(x,y))val[i]--;//它是黑点的第一个出现的左上角黑点,所以从现在开始不是一个满足条件的黑点 
        }
    }
}
void update(int x)
{
	sh[x].cnt=0;//直接清空 
    sh[x].mn=min(sh[x<<1].mn,sh[x<<1|1].mn);
    if(sh[x<<1].mn==sh[x].mn)sh[x].cnt+=sh[x<<1].cnt;
    if(sh[x<<1|1].mn==sh[x].mn)sh[x].cnt+=sh[x<<1|1].cnt;
    return ;
}
void build(int x,int l,int r)
{
    if(l==r)
    {
        sh[x].mn=val[l],sh[x].cnt=1;//最小值能否为0?不能,因为无论如何最左上的黑点一定符合黑点条件 
        return;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    update(x);
    return ;
}
void pushdown(int x)
{
    if(sh[x].lz==0)return;
    sh[x<<1].lz+=sh[x].lz,sh[x<<1].mn+=sh[x].lz;
    sh[x<<1|1].lz+=sh[x].lz,sh[x<<1|1].mn+=sh[x].lz;
    sh[x].lz=0;
    return;
}
void modify(int x,int l,int r,int lt,int rt,int val)
{
    if(l>rt||r<lt||rt<lt)return;
    if(l>=lt&&r<=rt)
    {
        sh[x].mn+=val;sh[x].lz+=val;
        return ;
    }
    pushdown(x);
    int mid=(l+r)>>1;
    if(lt<=mid)modify(x<<1,l,mid,lt,rt,val);
    if(rt>mid)modify(x<<1|1,mid+1,r,lt,rt,val);
    update(x);
}
int main()
{
    //ios::sync_with_stdio(false);
    // cin.tie(0); cout.tie(0);
    cin>>n>>m>>q;
    zs=n*m;
    for(int i=1;i<=zs;i++)
    {
        cin>>a[i].h>>a[i].l;
        a[i].h+=1,a[i].l+=1;//下标从1开始
        num(a[i].h,a[i].l)=i;//变黑的时间点
    }
    for(int i=1;i<=zs;i++)js(a[i].h,a[i].l,i);
    build(1,1,zs);
    for(int i=1;i<=q;i++)
    {
        int u,v,xx,cnt=0;cin>>u>>v;u+=1,v+=1;//不要忘记下标加一 
        if(u>v)swap(u,v);//确保u比v小 
        if(u==v){cout<<sh[1].cnt<<'\n';continue;}
        st[++cnt]=u;
        st[++cnt]=v;
        for(int j=0;j<=3;j++)
        {
            int x=a[u].h+dx[j],y=a[u].l+dy[j];
            if(x>=1&&x<=n&&y>=1&&y<=m)st[++cnt]=num(x,y);
            x=a[v].h+dx[j],y=a[v].l+dy[j];
            if(x>=1&&x<=n&&y>=1&&y<=m)st[++cnt]=num(x,y);
        }
        sort(st+1,st+cnt+1);
        for(int j=1;j<=cnt;j++)
        {
            if(st[j]==st[j-1])continue;//去重不能计算两遍? 
            if((xx=white(a[st[j]].h,a[st[j]].l))<st[j])//交换u,v对满足条件的白点减少情况(不存在不算) 
				modify(1,1,zs,max(u,xx),min(v,st[j])-1,-1);//如果是v周围的点,将v换成u,只会增加区间不会减少区间,所以只考虑u周围的点。
				//对u周围的u,v限制区间的话点u的贡献就是从u到st[j],v就是从v到st[j],但有可能不是由u,v限制的,所以取min,max 
            if((xx=black(a[st[j]].h,a[st[j]].l))>st[j])//交换u,v对满足条件的白点减少情况(不存在不算)
				modify(1,1,zs,max(u,st[j]),min(v,xx)-1,-1);//如果是u周围的点,将u换成v,只会增加区间不会减少区间
				//对v周围的区间,v的贡献是身体从st[j]到v,u的贡献就是从st[j]到u, 
        }
        swap(num(a[u].h,a[u].l),num(a[v].h,a[v].l));swap(a[u],a[v]);
        for(int j=1;j<=cnt;j++)
        {
            if(st[j]==st[j-1])continue;
            if((xx=white(a[st[j]].h,a[st[j]].l))<st[j])modify(1,1,zs,max(u,xx),min(v,st[j])-1,1);
            if((xx=black(a[st[j]].h,a[st[j]].l))>st[j])modify(1,1,zs,max(u,st[j]),min(v,xx)-1,1);
        }
        cout<<sh[1].cnt<<'\n';
    }
    return 0;
}

标签:IOI2018,val,int,xx,黑点,P4898,&&,seats,zs
From: https://www.cnblogs.com/storms11/p/18521781

相关文章

  • P4899 [IOI2018] werewolf 狼人 题解
    因为我记忆力不好,经常遇到之前做过的题一下子想不起来,所以打算基本上给每个比较有意思的题写题解,同时造福后代。这是werewolf,它是furry,很可爱题意:一张无向图,点有点权,每次询问从\(u\)到\(v\)的路径中,是否存在一条先走点权大于等于\(L\),再走点权小于等于\(R\)的路径。思路......
  • P4899 [IOI2018] werewolf 狼人 题解
    P4899[IOI2018]werewolf狼人题解题目描述省流:\(n\)个点,\(m\)条边,\(q\)次询问,对于每一次询问,给定一个起点\(S\)和终点\(T\),能否找到一条路径,前半程不能走\(0\thicksimL-1\)这些点,后半程不能走\(R+1\thicksimN-1\)这些点。中途必须有一个点在\(L\thicksimR\)之......
  • P5044 [IOI2018] meetings 会议 思考--zhengjun
    在NFLS模拟赛上遇到的,赛后订正过的。隔了蛮长时间的,总结一下。首先转化为笛卡尔树上后缀前缀的问题。然后考虑如何转移,发现转移形如\(f(x)=\min\{f(x)+C,kx+b\}\)的形式。可以直接线段树维护每个点的最优直线,在update的时候:如果\(f(x)+C\lekx+b\)恒成立(左右......
  • 【题解】P4898 [IOI2018] seats 排座位
    思路线段树。题意可以转化成每次判定有多少个前缀满足所有结点构成矩形。首先排除确定矩阵坐标再数答案的做法,因为太难。所以考虑如何对前缀进行判定。一个简单的想法是维护前\(i\)个点中\(x,y\)坐标的最值,但这样只能暴力看矩阵中的所有元素,跑得很慢。不妨思考一下合法......
  • 【题解】[IOI2018] werewolf 狼人
    题目分析这个题本身很简单,可能就是因为是IOI题所以看上去就难了些吧。其实题目就是让我们先走一段全部大于等于\(l\)的点然后再走一段全部小于等于\(r\)的点,那么能......
  • 【题解】P4899 [IOI2018] werewolf 狼人
    そうやってただ日が暮れるまで語り掛ける本当の言葉题意给定一个有向图和若干询问,每次询问是否存在一条满足条件的路径:端点分别为\(u,v\)前面一段不经过\([1,L......