Path to Integer_ABC402f分析与解答

考虑怎么降低复杂度,使用分治策略降低搜索的复杂度。

对于a_i,j,其一定在最后结果数的第(2n-i-j)位(如果将最低位看成第0位),故将a_i,j看成a_i,j * 10^(2n-i-j),这样每次加上a_i,j就可以了。

从(1,1)到(n,n)一定会经过右上-左下这条对角线上的点,这些点分别是(1,n),(2,n-1)....(n,1),从(1,1)走到这些点走的步数是n-1,从这些点走到(n,n)走的步数也是n-1,并且对于一个固定的对角线上的点p,从(1,1)走到p要走的横向步数和纵向步数是确定的,从p走到(n,n)要走的横向步数和纵向步数也是确定的,记录从(1,1)到p走出的所有结果,从小到大排序并去重,得到数组s1,同样记录从p到(n,n)走出的所有结果,从小到大排序并去重,得到数组s2,对s1中的每一个元素x,如果s2中的元素y使得x+y>=m,则这样的y不如使得x+y<m的y,在使得x+y<m的y中,我们选最大的y,如果没有使得x+y<m的y,就选x+y>=m的y中最大的

复杂度上限估计:NlogN

#include<bits/stdc++.h>
using namespace std;
using ll=long long ;

const ll N=40,maxn=25;
ll n,m,cnt1,cnt2;
ll md[maxn<<1],a[maxn][maxn];
vector<ll> s1,s2;

void dfs(ll x,ll y,ll xrem,ll yrem,ll sum,ll t){
    if(xrem==0 && yrem==0) {
        if(t==1) s1.push_back(sum);
        if(t==2) s2.push_back(sum);
        return;
    }
    if(xrem>0) dfs(x+1,y,xrem-1,yrem,(sum+a[x+1][y])%m,t);
    if(yrem>0) dfs(x,y+1,xrem,yrem-1,(sum+a[x][y+1])%m,t);
}

ll bin_search(ll top){
    ll ans=-1;
    ll l=0,r=cnt2;
    while(l<r) {
        ll mid=(l+r)>>1;
        if(s2[mid]<=top) {
            ans=s2[mid];
            l=mid+1;
        }else {
            r=mid;
        }
    }
    if(ans==-1) ans=s2[cnt2-1];
    return ans;
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);

    cin>>n>>m;
    md[0]=1%m;
    for(ll i=1;i<=N;i++) md[i]=md[i-1]*10%m;

    for(ll i=1;i<=n;i++){
        for(ll j=1;j<=n;j++){
            cin>>a[i][j];
            a[i][j]=a[i][j]%m*md[2*n-i-j]%m;
        }
    }

    ll ans=0;
    for(ll i=1;i<=n;i++){
        s1.clear();s2.clear();
        dfs(1,1,i-1,n-i,a[1][1],1);
        dfs(i,n-i+1,n-i,i-1,0,2);
        stable_sort(s1.begin(),s1.end());
        cnt1=unique(s1.begin(),s1.end())-s1.begin();
        stable_sort(s2.begin(),s2.end());
        cnt2=unique(s2.begin(),s2.end())-s2.begin();
        for(ll j=0;j<cnt1;j++){
            ll x=s1[j];
            ll top=m-1-x;
            ll y=bin_search(top);
            ans=max(ans,(x+y)%m);
        }
    }
    cout<<ans<<"\n";
    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值