[NOI2022] 二次整数规划问题

题意:
n n n个数, a i ∈ [ L i , R i ] ⊂ [ 1 , k ] , k < = 5 a_i\in[L_i,R_i]\subset[1,k],k<=5 ai∈[Li,Ri]⊂[1,k],k<=5,有 m m m个限制,满足 ∣ a x i − a y i ∣ < = z i |a_{x_i}-a_{y_i}|<=z_i ∣axi−ayi∣<=zi。设 G = ∑ i = 1 n ∑ j = 1 n [ ∣ a i − a j ∣ < = 1 ] G=\sum_{i=1}^n\sum_{j=1}^n [|a_i-a_j|<=1] G=∑i=1n∑j=1n[∣ai−aj∣<=1]让你对每组询问输出 1 0 6 G + ∑ i = 2 k − 1 v i ( ∑ j = 1 n [ a j = = i ] ) 10^6G+\sum_{i=2}^{k-1}v_i(\sum_{j=1}^n[a_j==i]) 106G+∑i=2k−1vi(∑j=1n[aj==i])的最大值。

思路:

这题必须要先掌握两个套路,一个是切糕[HNOI2013]切糕和最小乘积树。

我们直接认为k=5,因此k<5和k=5是一样的。

然后我们发现,除非必须要填1或者5的位置,都必须填2,3,4。只有值域为1或者5 ;或者和必须填1或5的数相等的数才需要填1或者5。因此我们可以预先求出来这些数,然后剩下的只用考虑填2的数和填4的数,就足以计算出式子。

我们设 ( x , y ) = ( s u m 2 , s u m 4 ) (x,y)=(sum_2,sum_4) (x,y)=(sum2,sum4),我们只用考虑这个式子的最终形式,为 − A x y + B x + C y + Z ( A > 0 ) -Axy+Bx+Cy+Z(A>0) −Axy+Bx+Cy+Z(A>0),配一下 − A ( x − a ) ( y − b ) + C -A(x-a)(y-b)+C −A(x−a)(y−b)+C,那么这个就是求矩形面积的最小值(我们认为面积为负数也是可以的)。这和最小乘积树是基本一样的。不过这个矩形平移后可能会到达每个象限。稍微思考你会发现无论矩形怎么平移,最优值一定在凸包上,直接无脑的把凸包整个找出来即可,只需要找到x最小和x最大,然后分治找即可。

下面介绍一种比较简单的方法,同时对上面分治的部分进行更加详细的解释。

所有点都坐落在 [ x m i n , x m a x ] ∗ [ y m i n , y m a x ] [x_{min},x_{max}]*[y_{min},y_{max}] [xmin,xmax]∗[ymin,ymax]之中,分析一下题目,很明显 ( x m i n , y m i n ) , ( x m i n , y m a x ) , ( x m a x , y m i n ) (x_{min},y_{min}),(x_{min},y_{max}),(x_{max},y_{min}) (xmin,ymin),(xmin,ymax),(xmax,ymin)都是一定可以取到的。

如果矩形和一,二,四象限有交点,那么只需要考虑这三个点;如果全部在第三象限,那么只用考虑上凸壳(当然你没有必要判断,你只需要把凸壳上的点求出来,然后全部求一次即可)。

考虑找上凸壳:

求出距离线段 A B AB AB最远的点。我们用差乘来求距离。令 A ( x 1 , y 1 ) , B ( x 2 , y 2 ) A(x_1,y_1),B(x_2,y_2) A(x1,y1),B(x2,y2),你要求的点是 C ( x , y ) C(x,y) C(x,y)。 A B → × A C → = ( x 2 − x 1 ) ( y − y 1 ) − ( x − x 1 ) ( y 2 − y 1 ) \overrightarrow{AB} \times\overrightarrow{AC}=(x_2-x_1)(y-y_1)-(x-x_1)(y_2-y_1) AB ×AC =(x2−x1)(y−y1)−(x−x1)(y2−y1),那么选择2的影响是 y 1 − y 2 y1-y2 y1−y2,选择4的影响是 x 2 − x 1 x2-x1 x2−x1,选择3的影响是 0 0 0,求最大值。为了方便用切糕模型求解,我们把三个统统取反,然后求最小值,如果有负数,那么就把全部加上一个值使三个数都是正数。

时间复杂度:

凸包的上界为 O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32),网络流时间复杂度 O ( n 2 l o g n + n m + q ) O(n^2logn+nm+q) O(n2logn+nm+q),可以通过此题(当然我也不知道是怎么证的,反正能过就可以)。

cpp 复制代码
#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){
    x=0;int f=0;char s=getchar();
    while(!isdigit(s))f|=s=='-',s=getchar();
    while(isdigit(s))x=x*10+s-48,s=getchar();
    x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){
    if(x<0)putchar('-'),x=-x;
    do{buf[++cc]=int(x%10);x/=10;}while(x);
    while(cc)putchar(buf[cc--]+'0');
}
const int N=1e3+10;
const ll inf=4e18,inff=4e17;
int n,m,Q,k;
struct node1{
    int x,y;
}a[N];
struct node2{
    int x,y,z;
}b[N*3];
bool one[N],five[N];
int s1,s234,s5;
vector<pair<int,int> >e[N];
int tot,hd[N<<2],cur[N<<2],ver[N<<5],nxt[N<<5];ll w[N<<5];
int h[N<<2],st,ed;
bool v[N<<2];
void add(int x,int y,ll z){
    tot++;
    ver[tot]=y;w[tot]=z;
    nxt[tot]=hd[x];
    hd[x]=tot;
}
void link(int x,int y,ll z){
    add(x,y,z),add(y,x,0);
}
bool bt_h(){
    memset(h,0,sizeof(h));h[st]=1;
    queue<int>q;q.push(st);
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=hd[x];i;i=nxt[i]){
            int y=ver[i];
            if(w[i]&&!h[y]){
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[ed]!=0;
}
ll dfs(int x,ll f){
    if(x==ed)return f;
    ll res=f,tt;
    for(int &i=cur[x];i;i=nxt[i]){
        int y=ver[i];
        if(w[i]&&h[y]==h[x]+1){
            tt=dfs(y,min(res,w[i]));
            w[i]-=tt,w[i^1]+=tt;
            res-=tt;if(!res)break;
        }
    }
    if(res==f)h[x]=0;
    return f-res;
}
ll dinic(){
    ll flow=0;
    while(bt_h()){
        memcpy(cur,hd,sizeof(cur));
        flow+=dfs(st,inf);
    }
    return flow;
}
ll val[7];int vid[N][7],eid[N<<2];
void calc(ll &ans,ll &sum2,ll &sum4){
    tot=1;memset(hd,0,sizeof(hd));
    st=6*n+1,ed=st+1;
    rep(i,1,n)link(st,i,inff);
    rep(i,5*n+1,5*n+n)link(i,ed,inff);
    int num=0;
    rep(j,1,6)rep(i,1,n)vid[i][j]=++num;
    rep(i,1,n){
        rep(j,1,5){
            if(j<a[i].x||j>a[i].y){
                link(vid[i][j],vid[i][j+1],inff);
            }
            else{
                link(vid[i][j],vid[i][j+1],val[j]);
            }
        }
    }
    rep(i,1,m){
        int x=b[i].x,y=b[i].y,z=b[i].z;
        if(x==y)continue;
        rep(j,z+2,5){
            link(vid[x][j],vid[y][j-z],inff);
            link(vid[y][j],vid[x][j-z],inff);
        }
    }
    ans=dinic();
    memset(v,0,sizeof(v));
    queue<int>q;q.push(st);v[st]=1;
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=hd[x];i;i=nxt[i]){
            int y=ver[i];
            if(w[i]&&!v[y]){
                v[y]=1;
                q.push(y);
            }
        }
    }
    sum2=0,sum4=0;
    rep(p1,1,n)rep(p2,1,5){
        int x=vid[p1][p2];
        if(!v[x])continue;
        for(int i=hd[x];i;i=nxt[i]){
            int y=ver[i];
            if(y!=st&&y!=ed&&(y-1)%n+1==p1&&(y-1)/n+1==p2+1&&!v[y]){
                if(p2==2)sum2++;
                if(p2==4)sum4++;
            }
        }
    }
}
struct point{
    ll x,y;
    point(ll xx=0,ll yy=0):x(xx),y(yy){}
}c[N];int cnt;
ll det(ll x1,ll y1,ll x2,ll y2){
    return x1*y2-x2*y1;
}
void cdq(ll x1,ll y1,ll x2,ll y2){
    memset(val,0,sizeof(val));
    val[2]=y2-y1,val[4]=x1-x2;
    ll t=max(-val[2],-val[4]);
    if(t>0){
        rep(i,1,5)val[i]+=t;
    }
    ll x,y;
    calc(t,x,y);
    if(det(x2-x1,y2-y1,x-x1,y-y1)<=0)return;
    c[++cnt]=point(x,y);
    cdq(x1,y1,x,y);
    cdq(x,y,x2,y2);
}
ll Cnt[7];
ll query(){
    ll G=0,ret=0;
    rep(i,1,5){
        G+=Cnt[i]*Cnt[i];
        if(i>1)G+=Cnt[i]*Cnt[i-1];
        if(i<5)G+=Cnt[i]*Cnt[i+1];
        ret+=Cnt[i]*val[i];
    }
    return G*1000000+ret;
}
void solve(){
    qr(k),qr(n),qr(m),qr(Q);
    rep(i,1,n)qr(a[i].x),qr(a[i].y);
    rep(i,1,m)qr(b[i].x),qr(b[i].y),qr(b[i].z);
    rep(i,1,n)e[i].clear();
    rep(i,1,m){
        e[b[i].x].push_back(make_pair(b[i].y,b[i].z));
        e[b[i].y].push_back(make_pair(b[i].x,b[i].z));
    }
    memset(one,0,sizeof(one));
    memset(five,0,sizeof(five));
    s1=s5=0;
    queue<int>q;
    rep(i,1,n)if(a[i].y==1){
        q.push(i);
        one[i]=1;s1++;
    }
    while(q.size()){
        int x=q.front();q.pop();
        for(auto &[y,z]:e[x]){
            if(!z&&!one[y]){
                one[y]=1;
                s1++;
                q.push(y);
            }
        }
    }
    rep(i,1,n)if(a[i].x==5){
        q.push(i);
        five[i]=1;s5++;
    }
    while(q.size()){
        int x=q.front();q.pop();
        for(auto &[y,z]:e[x]){
            if(!z&&!five[y]){
                five[y]=1;
                s5++;
                q.push(y);
            }
        }
    }
    s234=n-s1-s5;
    rep(i,1,n){
        if(!one[i])a[i].x=max(a[i].x,2);
        if(!five[i])a[i].y=min(a[i].y,4);
    }
    ll res,sum2,sum4;
    ll minx,maxx,miny,maxy;
    memset(val,0,sizeof(val));
    val[2]=1;
    calc(res,sum2,sum4);
    minx=sum2;
    val[2]=0;

    val[4]=1;
    calc(res,sum2,sum4);
    miny=sum4;
    val[4]=0;

    val[3]=val[4]=1;
    calc(res,sum2,sum4);
    maxx=sum2;
    val[3]=val[4]=0;

    val[2]=val[3]=1;
    calc(res,sum2,sum4);
    maxy=sum4;
    val[2]=val[3]=0;

    cnt=0;
    c[++cnt]=point(minx,miny);
    c[++cnt]=point(minx,maxy);
    c[++cnt]=point(maxx,miny);
    cdq(minx,maxy,maxx,miny);
    // cout<<"c:\n";
    // rep(i,1,cnt)cout<<c[i].x<<" "<<c[i].y<<endl;
    // cout<<"---------\n";
    while(Q--){
        memset(val,0,sizeof(val));
        rep(i,2,k-1)qr(val[i]);
        memset(Cnt,0,sizeof(Cnt));
        Cnt[1]=s1;Cnt[5]=s5;
        ll ans=-1;
        rep(i,1,cnt){
            Cnt[2]=c[i].x;
            Cnt[4]=c[i].y;
            Cnt[3]=s234-Cnt[2]-Cnt[4];
            ans=max(ans,query());
        }
        qw(ans);puts("");
    }
}
int main(){
    // freopen("qip8.in","r",stdin);
    // freopen("qip8.out","w",stdout);
    int tt;qr(tt);qr(tt);
    while(tt--)solve();
    return 0;
}
相关推荐
Fabian!5 分钟前
代码随想录训练营Day29 | 93.复原IP地址 | 78.子集 | 90.子集II
算法
冰淇淋加点糖24 分钟前
C语言之扫雷小游戏(完整代码版)
c语言·前端·算法
luthane41 分钟前
python 实现贪婪合并排序算法
python·算法·排序算法
3D打印技术参考1 小时前
激光增材制造新突破:精细调控NiTi合金弹热效应,实现制冷定制
人工智能·算法·计算机视觉·制造
专利爱好者小米粥1 小时前
如何成为专利检索高手?
物联网·算法·microsoft·区块链·数据库架构
通信仿真实验室2 小时前
(19)MATLAB使用Nakagami 分布对象生成Nakagami-m分布
开发语言·算法·matlab
白白白白白kkk2 小时前
【力扣算法题】每天一道,健康生活
算法·leetcode
硕风和炜3 小时前
【LeetCode: 134. 加油站 | 贪心算法】
java·算法·leetcode·贪心算法
sjsjs114 小时前
【动态规划-最长公共子序列(LCS)】力扣97. 交错字符串
算法·leetcode·动态规划