P9120 题解

暴力容斥复活之路!

\(k=1\)

这个你肯定会。

\(k=2\)

大的放上去,小的放下来。简单贪心。

\(k=3\)

考虑二分答案。

然后考虑判断是否合法。

令当前答案为 \(val\)

首先钦定最小值在第一行。

然后枚举最大值在哪一行。

现在我们就确定了两行可以填的数的范围。

剩下一行的选择就出来了。

现在问题是怎么判断剩下一行是否合法。

考虑把是否有一个长度为答案的区间覆盖所有点的问题转变为把每个点 \(x\) 对区间 \([x,x+val]\) 做贡献。

然后看有没有点被贡献了 \(n\) 遍。

但是同一个选择的点会重复贡献。

所以我们直接暴力容斥去除重复贡献。

这里由于值域很小,直接用差分维护。

\(k=4\)

\(k=3\) 的做法差不多。

一样的做法,把每个点对一个正方形,然后暴力容斥。

但是问题变成了二维数点。所以离线用线段树维护。

但是被贡献 \(n\) 次的点有 \(V^2\) 种情况,怎么办?

发现被贡献 \(n\) 次的点一定是被贡献最多的点,所以在线段树上维护前缀和极值就可以了。

卡常

上面的做法复杂度是 \(O(k^2 \times 2^k \times n \times \log^{k-2} (n \times 2^k))\)

那么开始卡常:

  1. 大量使用位运算。

  2. 记录最大访问到的位置,减少对没有访问的数组的清空。

  3. 因为线段树只用维护单点修改,所以使用非递归线段树。

上面是核心的卡常思路,剩下一些小优化就看代码吧(15KB 慎入)。

代码

#include<bits/stdc++.h>
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
using namespace std;
namespace IO{
    const int SIZE=1<<21;
    static char ibuf[SIZE],obuf[SIZE],*iS,*iT,*oS=obuf,*oT=oS+SIZE-1;
    int qr;
    char qu[55],c;
    bool f;
    #define getchar() (IO::iS==IO::iT?(IO::iT=(IO::iS=IO::ibuf)+fread(IO::ibuf,1,IO::SIZE,stdin),(IO::iS==IO::iT?EOF:*IO::iS++)):*IO::iS++)
    #define putchar(x) *IO::oS++=x,IO::oS==IO::oT?flush():0
    #define flush() fwrite(IO::obuf,1,IO::oS-IO::obuf,stdout),IO::oS=IO::obuf
    #define puts(x) IO::Puts(x)
    template<typename T>
    inline void read(T&x){
        for(f=1,c=getchar();c<48||c>57;c=getchar())f^=c=='-';
        for(x=0;c<=57&&c>=48;c=getchar()) x=(x<<1)+(x<<3)+(c&15); 
        x=f?x:-x;
    }
    template<typename T>
    inline void write(T x){
        if(!x) putchar(48); if(x<0) putchar('-'),x=-x;
        while(x) qu[++qr]=x%10^48,x/=10;
        while(qr) putchar(qu[qr--]);
    }
    inline void Puts(const char*s){
        for(int i=0;s[i];++i)
            putchar(s[i]);
        putchar('\n');
    }
    struct Flusher_{~Flusher_(){flush();}}io_flusher_;
}
using IO::read;
using IO::write;
const int maxk = 5,maxn = 5e4+14,maxv=3e4+10;
int a[maxn][maxk];
int k;
int ans(int j,int n){
    int mx=0,mi=65535;
    for(int i=1;i<=n;++i) mx=max(mx,a[i][j]),mi=min(mi,a[i][j]);
    return mx-mi;
}
vector<int> ch[maxn];//剩下一行可能的选择
int sum[maxv];//差分数组
int n;
int lwx;
inline void add(int l,int r,int val){//差分数组上修改
    r=min(r,maxv-7);
    sum[l]+=val;
    sum[r+1]-=val;
    lwx=max(lwx,r+1);
}
inline void maintain(){//统计差分数组
    for(register int i=1;i<=lwx;i=-~i) sum[i]+=sum[i-1];
}
bool check(int pos,int mx,int mi,int val){//最大值放在第 1 行,最小值放在第 pos 行 k=3 极值为 val 是否合法
    for(register int i=0;i<=lwx;i=-~i) sum[i]=0;
    lwx=0;
    for(register int i=1;i<=n;i=-~i) ch[i].clear();
    int r1=mx,l1=r1-val;
    int l2=mi,r2=l2+val;
    for(register int i=1;i<=n;i=-~i){
        if(l1<=a[i][1]&&a[i][1]<=r1&&l2<=a[i][pos]&&a[i][pos]<=r2){//确定区间的行填的数合法合法
            ch[i].push_back(a[i][((pos-2)^1)+2]);
        }
        a[i][1]=a[i][1]^a[i][2];
        a[i][2]=a[i][1]^a[i][2];
        a[i][1]=a[i][1]^a[i][2];
        a[i][1]=a[i][1]^a[i][3];
        a[i][3]=a[i][1]^a[i][3];
        a[i][1]=a[i][1]^a[i][3];
        //转锁调整数组
        if(l1<=a[i][1]&&a[i][1]<=r1&&l2<=a[i][pos]&&a[i][pos]<=r2){//确定区间的行填的数合法合法
            ch[i].push_back(a[i][((pos-2)^1)+2]);
        }
        a[i][1]=a[i][1]^a[i][2];
        a[i][2]=a[i][1]^a[i][2];
        a[i][1]=a[i][1]^a[i][2];
        a[i][1]=a[i][1]^a[i][3];
        a[i][3]=a[i][1]^a[i][3];
        a[i][1]=a[i][1]^a[i][3];
        if(l1<=a[i][1]&&a[i][1]<=r1&&l2<=a[i][pos]&&a[i][pos]<=r2){//确定区间的行填的数合法合法
            ch[i].push_back(a[i][((pos-2)^1)+2]);
        }
        if(ch[i].size()==0) return false;
        if(ch[i].size()==1){
            add(ch[i][0],ch[i][0]+val,1);
        }
        else if(ch[i].size()==2){
            add(ch[i][0],ch[i][0]+val,1);
            add(ch[i][1],ch[i][1]+val,1);
            int l=max(ch[i][0],ch[i][1]),r=min(ch[i][0]+val,ch[i][1]+val);
            if(l<=r)
                add(l,r,-1);
            //容斥处理
        }
        else {
            int l=min(ch[i][0],min(ch[i][1],ch[i][2])),r=mx;
            add(l,r,1);
        }
    }
    maintain();
    for(register int i=1;i<=n;i=-~i){
        if(ch[i].size()==0) continue;
        for(register int u:ch[i])
            if(sum[u]==n)//被所有区间包含,也就是区间 [u-val,u] 包含所有点
                return true;
    }   
    return false;
}
vector<int> wyb;
int tree[65536+10][2];//sum ans 和:最大前缀和 
vector< pair<int,int> > opt[maxn]; //pos:val
int top;
inline void add_p(pair<int,int> A,pair<int,int> B,int val){
    B.first=min(B.first,top);
    B.second=min(B.second,top);
    opt[B.first].push_back(make_pair(B.second,val));
    opt[A.first-1].push_back(make_pair(B.second,-val));
    opt[B.first].push_back(make_pair(A.second-1,-val));
    opt[A.first-1].push_back(make_pair(A.second-1,val));
    wyb.push_back(B.first);
    wyb.push_back(A.first-1);
    //二维差分 
}
//值域平移 1 
vector< pair<int,int> > chifan[maxn];
bool Check(int pos,int mx,int mi,int val){//最大值放在第 1 行,最小值放在第 pos 行 k=4 极值为 val 是否合法
    for(int u:wyb) opt[u].clear();
    wyb.clear();
    top=mx+2;
    memset(tree,0,sizeof(tree));
    for(register int i=1;i<=n;i=-~i) chifan[i].clear();
    int r1=mx,l1=r1-val;
    int l2=mi,r2=l2+val;
    int last;
    for(register int i=1;i<=n;i=-~i){
        if(l1<=a[i][1]&&a[i][1]<=r1&&l2<=a[i][pos]&&a[i][pos]<=r2){//确定区间的行填的数合法合法
            if(pos==2){
                chifan[i].push_back(make_pair(a[i][3],a[i][4]));
            }
            else if(pos==3){
                chifan[i].push_back(make_pair(a[i][2],a[i][4]));    
            }
            else{
                chifan[i].push_back(make_pair(a[i][2],a[i][3]));
            }
        }
        a[i][1]=a[i][1]^a[i][2];
        a[i][2]=a[i][1]^a[i][2];
        a[i][1]=a[i][1]^a[i][2];
        a[i][1]=a[i][1]^a[i][3];
        a[i][3]=a[i][1]^a[i][3];
        a[i][1]=a[i][1]^a[i][3];
        a[i][1]=a[i][1]^a[i][4];
        a[i][4]=a[i][1]^a[i][4];
        a[i][1]=a[i][1]^a[i][4];
        if(l1<=a[i][1]&&a[i][1]<=r1&&l2<=a[i][pos]&&a[i][pos]<=r2){//确定区间的行填的数合法合法
            if(pos==2){
                chifan[i].push_back(make_pair(a[i][3],a[i][4]));

            }
            else if(pos==3){
                chifan[i].push_back(make_pair(a[i][2],a[i][4]));

            }
            else{
                chifan[i].push_back(make_pair(a[i][2],a[i][3]));
            }
        }
        a[i][1]=a[i][1]^a[i][2];
        a[i][2]=a[i][1]^a[i][2];
        a[i][1]=a[i][1]^a[i][2];
        a[i][1]=a[i][1]^a[i][3];
        a[i][3]=a[i][1]^a[i][3];
        a[i][1]=a[i][1]^a[i][3];
        a[i][1]=a[i][1]^a[i][4];
        a[i][4]=a[i][1]^a[i][4];
        a[i][1]=a[i][1]^a[i][4];
        if(l1<=a[i][1]&&a[i][1]<=r1&&l2<=a[i][pos]&&a[i][pos]<=r2){//确定区间的行填的数合法合法
            if(pos==2){
                chifan[i].push_back(make_pair(a[i][3],a[i][4]));

            }
            else if(pos==3){
                chifan[i].push_back(make_pair(a[i][2],a[i][4]));

            }
            else{
                chifan[i].push_back(make_pair(a[i][2],a[i][3]));
            }
        }
        a[i][1]=a[i][1]^a[i][2];
        a[i][2]=a[i][1]^a[i][2];
        a[i][1]=a[i][1]^a[i][2];
        a[i][1]=a[i][1]^a[i][3];
        a[i][3]=a[i][1]^a[i][3];
        a[i][1]=a[i][1]^a[i][3];
        a[i][1]=a[i][1]^a[i][4];
        a[i][4]=a[i][1]^a[i][4];
        a[i][1]=a[i][1]^a[i][4];
        if(l1<=a[i][1]&&a[i][1]<=r1&&l2<=a[i][pos]&&a[i][pos]<=r2){//确定区间的行填的数合法合法
            if(pos==2){
                chifan[i].push_back(make_pair(a[i][3],a[i][4]));    
            }
            else if(pos==3){
                chifan[i].push_back(make_pair(a[i][2],a[i][4]));        
            }
            else{
                chifan[i].push_back(make_pair(a[i][2],a[i][3]));
            }
        }
        //下面开始容斥处理贡献 
        if(chifan[i].size()==0) return false;
        if(chifan[i].size()==1){
            add_p(chifan[i][0],make_pair(chifan[i][0].first+val,chifan[i][0].second+val),1);
        }
        else if(chifan[i].size()==2){
            add_p(chifan[i][0],make_pair(chifan[i][0].first+val,chifan[i][0].second+val),1);
            add_p(chifan[i][1],make_pair(chifan[i][1].first+val,chifan[i][1].second+val),1);
            pair<int,int> l=make_pair(max(chifan[i][0].first,chifan[i][1].first),max(chifan[i][0].second,chifan[i][1].second)),r=make_pair(min(chifan[i][0].first+val,chifan[i][1].first+val),min(chifan[i][0].second+val,chifan[i][1].second+val));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);              
            //容斥处理
        }
        else if(chifan[i].size()==3){
            add_p(chifan[i][0],make_pair(chifan[i][0].first+val,chifan[i][0].second+val),1);
            add_p(chifan[i][1],make_pair(chifan[i][1].first+val,chifan[i][1].second+val),1);
            add_p(chifan[i][2],make_pair(chifan[i][2].first+val,chifan[i][2].second+val),1);
            pair<int,int> l=make_pair(max(chifan[i][0].first,chifan[i][1].first),max(chifan[i][0].second,chifan[i][1].second)),r=make_pair(min(chifan[i][0].first+val,chifan[i][1].first+val),min(chifan[i][0].second+val,chifan[i][1].second+val));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);
            l=make_pair(max(chifan[i][0].first,chifan[i][2].first),max(chifan[i][0].second,chifan[i][2].second)),r=make_pair(min(chifan[i][0].first+val,chifan[i][2].first+val),min(chifan[i][0].second+val,chifan[i][2].second+val));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);
            l=make_pair(max(chifan[i][1].first,chifan[i][2].first),max(chifan[i][1].second,chifan[i][2].second)),r=make_pair(min(chifan[i][1].first+val,chifan[i][2].first+val),min(chifan[i][1].second+val,chifan[i][2].second+val));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);
            l=make_pair(max(chifan[i][0].first,max(chifan[i][1].first,chifan[i][2].first)),max(chifan[i][0].second,max(chifan[i][1].second,chifan[i][2].second))),r=make_pair(min(chifan[i][0].first+val,min(chifan[i][1].first+val,chifan[i][2].first+val)),min(chifan[i][0].second+val,min(chifan[i][1].second+val,chifan[i][2].second+val)));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,1);
        }
        else{
            add_p(chifan[i][0],make_pair(chifan[i][0].first+val,chifan[i][0].second+val),1);
            add_p(chifan[i][1],make_pair(chifan[i][1].first+val,chifan[i][1].second+val),1);
            add_p(chifan[i][2],make_pair(chifan[i][2].first+val,chifan[i][2].second+val),1);
            add_p(chifan[i][3],make_pair(chifan[i][3].first+val,chifan[i][3].second+val),1);
            pair<int,int> l=make_pair(max(chifan[i][0].first,chifan[i][1].first),max(chifan[i][0].second,chifan[i][1].second)),r=make_pair(min(chifan[i][0].first+val,chifan[i][1].first+val),min(chifan[i][0].second+val,chifan[i][1].second+val));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);
            l=make_pair(max(chifan[i][0].first,chifan[i][2].first),max(chifan[i][0].second,chifan[i][2].second)),r=make_pair(min(chifan[i][0].first+val,chifan[i][2].first+val),min(chifan[i][0].second+val,chifan[i][2].second+val));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);
            l=make_pair(max(chifan[i][1].first,chifan[i][2].first),max(chifan[i][1].second,chifan[i][2].second)),r=make_pair(min(chifan[i][1].first+val,chifan[i][2].first+val),min(chifan[i][1].second+val,chifan[i][2].second+val));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);
            l=make_pair(max(chifan[i][0].first,chifan[i][3].first),max(chifan[i][0].second,chifan[i][3].second)),r=make_pair(min(chifan[i][0].first+val,chifan[i][3].first+val),min(chifan[i][0].second+val,chifan[i][3].second+val));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);
            l=make_pair(max(chifan[i][1].first,chifan[i][3].first),max(chifan[i][1].second,chifan[i][3].second)),r=make_pair(min(chifan[i][1].first+val,chifan[i][3].first+val),min(chifan[i][1].second+val,chifan[i][3].second+val));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);
            l=make_pair(max(chifan[i][2].first,chifan[i][3].first),max(chifan[i][2].second,chifan[i][3].second)),r=make_pair(min(chifan[i][2].first+val,chifan[i][3].first+val),min(chifan[i][2].second+val,chifan[i][3].second+val));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);
            l=make_pair(max(chifan[i][0].first,max(chifan[i][1].first,chifan[i][2].first)),max(chifan[i][0].second,max(chifan[i][1].second,chifan[i][2].second))),r=make_pair(min(chifan[i][0].first+val,min(chifan[i][1].first+val,chifan[i][2].first+val)),min(chifan[i][0].second+val,min(chifan[i][1].second+val,chifan[i][2].second+val)));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,1);
            l=make_pair(max(chifan[i][1].first,max(chifan[i][2].first,chifan[i][3].first)),max(chifan[i][1].second,max(chifan[i][2].second,chifan[i][3].second))),r=make_pair(min(chifan[i][1].first+val,min(chifan[i][2].first+val,chifan[i][3].first+val)),min(chifan[i][1].second+val,min(chifan[i][2].second+val,chifan[i][3].second+val)));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,1);
            l=make_pair(max(chifan[i][0].first,max(chifan[i][2].first,chifan[i][3].first)),max(chifan[i][0].second,max(chifan[i][2].second,chifan[i][3].second))),r=make_pair(min(chifan[i][0].first+val,min(chifan[i][2].first+val,chifan[i][3].first+val)),min(chifan[i][0].second+val,min(chifan[i][2].second+val,chifan[i][3].second+val)));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,1);
            l=make_pair(max(chifan[i][0].first,max(chifan[i][1].first,chifan[i][3].first)),max(chifan[i][0].second,max(chifan[i][1].second,chifan[i][3].second))),r=make_pair(min(chifan[i][0].first+val,min(chifan[i][1].first+val,chifan[i][3].first+val)),min(chifan[i][0].second+val,min(chifan[i][1].second+val,chifan[i][3].second+val)));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,1);
            l=make_pair(max(chifan[i][0].first,max(chifan[i][1].first,max(chifan[i][2].first,chifan[i][3].first))),max(chifan[i][0].second,max(chifan[i][1].second,max(chifan[i][2].second,chifan[i][3].second)))),r=make_pair(min(chifan[i][0].first+val,min(chifan[i][1].first+val,min(chifan[i][2].first+val,chifan[i][3].first+val))),min(chifan[i][0].second+val,min(chifan[i][1].second+val,min(chifan[i][2].second+val,chifan[i][3].second+val))));
            if(l.first<=r.first&&l.second<=r.second)
                add_p(l,r,-1);
        }
    }
    for(register int i=top;i>=0;i=-(-~(-~(~(i))))){
        if(opt[i].size()==0) continue;
        for(pair<int,int> u:opt[i]){
            int x=u.first+32768;
            tree[x][0]+=u.second;
            tree[x][1]+=u.second;
            x>>=1;
            while(x!=0){
                tree[x][0]=tree[x<<1][0]+tree[x<<1|1][0];
                tree[x][1]=min(tree[x<<1][1],tree[x<<1][0]+tree[x<<1|1][1]);
                x>>=1;
            }
        }
        if(tree[1][0]-tree[1][1]>=n) return true;
    }
    return false;
}
void work(){
    read(n);
    int mx=0,mi=maxv;
    for(register int i=1;i<=k;++i){
        for(register int j=1;j<=n;++j){
            read(a[j][i]);
            mx=max(mx,a[j][i]);
            mi=min(mi,a[j][i]);
        }
    }
    if(k==1){
        write(ans(1,n));
        putchar('\n');
    }
    else if(k==2){
        for(register int i=1;i<=n;++i){
            if(a[i][1]<a[i][2]) swap(a[i][1],a[i][2]);
        }
        write(max(ans(1,n),ans(2,n)));
        putchar('\n');
    }
    else if(k==3){
        int l=-1,r=(mx-mi);
        while(l+1<r){
            int mid=(l+r)>>1;
            if(check(2,mx,mi,mid)==true||check(3,mx,mi,mid)==true){
                r=mid;
            }
            else{
                l=mid;
            }
        }
        write(r);
        putchar('\n');
    }
    else{
        int l=-1,r=(mx-mi);
        while(l+1<r){
            int mid=(l+r)>>1;
            if(Check(4,mx,mi,mid)==true||Check(3,mx,mi,mid)==true||Check(2,mx,mi,mid)==true){
                r=mid;
            }
            else{
                l=mid;
            }
        }
        write(r);
        putchar('\n');
    }
    return ;
}
signed main(){
    int t;
    read(t);
    read(k);
    while(t--) work();
}
posted @ 2024-01-30 23:56  ChiFAN鸭  阅读(12)  评论(0编辑  收藏  举报