1 条题解

  • 0
    @ 2026-5-3 8:16:45

    数学题?观察到性质还挺好写的。

    分析

    首先认真审题,题目说的是包含一个由 nn 个十进制数位组成的整数 xx,所以最大是十万位,用 int 或者 long long 存储显然是不现实的,所以用高精度存储答案和 int128 在中间运算。

    接下来就是思路处理了:

    题目只要求找到

    因为这道题的 yy1y10161\le y\le10^{16})。
    我们不妨从一个数字移动的位数考虑,找到一个移动位数可以让交换产生的价值完全覆盖代价。 不妨假想一种极限情况,现在有一串数字 199999999999999921999999999999999211 是第十七位,22 是第一位,将 22 交换到第十七位本身能带来 2×1015+72\times 10^{15}+7 的价值。
    现在如果交换一次的花销是 101610^{16},因为我们进行了 1616 次交换,所以总贡献是负数。

    接下来就是找到一个让这个代价能被覆盖的位置,可以找到:
    $2199999999999999999 - 1999999999999999992 - 18 \times 10^{16} = 20000000000000007$。
    此时 11 在第十九位,这就是我们要找的位数。

    也就是说:当一个数字移动到了第十九位之前(包括十九),一定可以为我带来正价值
    扩展来说:第十九位之前的从大到小排序,一定可以为我带来正价值

    更进一步的讲,我们可以将整个字符串先整体从大到小排序,然后将十九位之后的按照原本的相对顺序排序,最后针对后十八位暴力就可以了。

    当然你不放心可以枚举后面二十位,我代码也是按照二十写的。
    暴力算的方式因人而异,这里提供我教练上课教的

    实现

    #include<bits/stdc++.h>
    using namespace std;
    #define int __int128
    inline int read(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){
    		if(ch=='-')f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9'){
    		x=x*10+ch-48;
    		ch=getchar();
    	}
    	return x*f;
    }
    void write(int x){
    	if(x<0){
    		putchar('-');
    		x=-x;
    	}
    	if(x>9){
    		write(x/10);
    	}
    	putchar(x%10+'0');
    }
    const int N=1e5+5;
    struct Node{
    	int v,id;
    	bool operator < (const Node &rhs)const{
    		return id<rhs.id;
    	} 
    }a[N];
    bool cmp(const Node &a,const Node &b){
    	if(a.v==b.v)return a.id<b.id;
    	return a.v>b.v;
    }
    string x;
    int y,ans[N],p[N],cnt,ans1[N],maxn,mcur;
    signed main(){
    	cin>>x;
    	y=read();
    	int len=x.size();
    	int l=max(len-20,(int)1);
    	x=' '+x;
    	for(int i=1;i<=len;i++){
    		ans[i]=x[i]-'0';//转化成数字,方便计算
    		a[i].v=ans[i];
    		a[i].id=i;//后面排序用
    	}
    	if(len>20){
    		sort(a+1,a+len+1,cmp);//整体排序
    		sort(a+len-20+1,a+len+1);//按原相对顺序排列后20位
    	}
    	for(int i=1;i<=len;i++){
    		ans[i]=a[i].v;
    	}
    	for(int i=l;i<=len;i++){
    		p[i]=ans[i];//这里记录原本后面20位的顺序,暴力枚举的时候初始化用
    	}
    	for(int k=0;k<=20*20+5;k++){
    		int curk=k,cur=0;
    		for(int i=l;i<=len;i++){
    			ans[i]=p[i];
    		}
    		for(int i=l;i<=len;i++){
    			cnt=0;
    			for(int j=i;j<=len;j++){
    				if(ans[j]>ans[cnt]&&curk>=j-i){
    					cnt=j;
    				}
    			}
    			if(cnt){
    				curk=curk-cnt+i;
    			}
    			for(int j=cnt;j>i;j--){
    				swap(ans[j],ans[j-1]);
    			}
    		}
    		for(int i=l;i<=len;i++){
    			cur=cur*10+ans[i];
    		}
    		if((cur-k*y>maxn)||(cur-k*y==maxn&&cur>mcur)){//当前情况更优,更新我的答案
    			maxn=cur-k*y;
    			mcur=cur;
    			for(int i=l;i<=len;i++){
    				ans1[i]=ans[i];//ans1存储的是后20位的顺序
    			}
    		}
    	}
        //二十位之前在ans中,后面的在ans1,输出即可
    	for(int i=1;i<l;i++){
    		write(ans[i]);
    	}
    	for(int i=l;i<=len;i++){
    		write(ans1[i]);
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    7142
    时间
    1000ms
    内存
    512MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者