BITACM 1043 缘分

这题算是中难题吧。

如果是暴力统计的方法,复杂度O(n^2),必TLE。

将题目的模型抽象出来,我们要做的就是给你若干个n维的点,确定里面最近的两个点(这里的距离定义为曼哈顿距离),并输出点的标号。

我们先考虑二维的情况。

假定有两个点(x1,y1),(x2,y2)。

则dis=|x1-x2|+|y1-y2|。

考虑将其展开,那么一共有四种情况,分别是:

x1-x2+y1-y2

x1-x2+y2-y1

x2-x1+y1-y2

x2-x1+y2-y1

再考虑将x1与y1放在一起,x2与y2放在一起,那么这四个式子可以变换为:

x1+y1+(-x2-y2)

x1-y1+(-x2+y2)

-x1+y1+x2-y2

-x1-y1+(x2+y2)

再变化一下

x1+y1-(x2+y2)

x1-y1-(x2-y2)

-x1+y1-(-x2+y2)

-x1-y1-(-x2-y2)

而我们注意到,真正的dis一定是对应着这四个式子里面其中的一个的,也是值最大的那个。

那么对于二维的情况,我们可以考虑将每个点枚举4个状态,取每个状态中的最大值和最小值作差,再在四个状态中取最大值即可。

推广到k维的情况,我们只要枚举每个点的2^k个状态,然后执行上述的操作就好了。

最终的复杂度是n*2^k。

如果你拿这个复杂度来写的话,我是TLE了。

我们可以考虑进行折半枚举。

二进制当中有着一定的对应关系,其实我们不需要枚举2^k个状态,这需要枚举2^(k-1)个状态就可以了。

#include "cstdio"
#include "algorithm"
using namespace std;
#define INF 999999
int n,m;
int fp,fa,fm;
int loc[10];
struct node{
    int v,no;
}a[1<<7][10005];
void input(){
    scanf("%d%d",&n,&m);
    scanf("%d%d%d",&fp,&fa,&fm);
    for(int i=0;i<n;i++){
        scanf("%d",&loc[0]);
        loc[1]=loc[0]%fm;
        for(int j=2;j<=m;j++){
            loc[j]=(loc[j-1]*fp+fa)%fm;
        }
        for(int s=0;s<1<<(m-1);s++){
            a[s][i].v=0;
            for(int k=0;k<m;k++){
                if((1<<k)&s)  a[s][i].v+=loc[k+1];
                else  a[s][i].v-=loc[k+1];
            }
            a[(1<<m)-1-s][i].v=-a[s][i].v;
        }
    }
}
void solve(){
    int ans,mx,mn;
    int lc1,lc2;
    int t1,t2;
    ans=-1;
    for(int s=0;s<1<<m;s++){
        mx=-INF,mn=INF;
        for(int i=0;i<n;i++){
            if(a[s][i].v>mx)  {mx=a[s][i].v;lc2=i;}
            if(a[s][i].v<mn)  {mn=a[s][i].v;lc1=i;}
        }
        if(mx-mn>ans){
            ans=mx-mn;
            t1=lc1,t2=lc2;
        }
    }
    if(t1>t2)  swap(t1,t2);
    if(t1==t2)  t2++;
    printf("%d %d %d\n",ans,t1+1,t2+1);
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        input();
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值