题目描述:
有一个长度为 L 的字符串,每个字符是大写字母。如果我们把 A看做 0 ,B 看做 1,C看做 2... Z 看做 25,那么我们就得到了一个 26 进制的数字串。
我们可以对这个字符串做一个操作:将两个位置的字母进行交换。这样得到了一个新的数字串。
现在有一个十进制整数 M ,请判断是否可以通过做至多一次(可以不做)操作,使得得到的字符串是 M 的倍数。
输入格式
第一行一个只包含大写字母的字符串。
第二行一个整数 M 。
输出格式
如果初始串就可以,那么输出 “0 0”
(不加引号)
如果通过一次操作可以,请输出交换的两个位置的标号(标号小的在前,从 1开始)。如果有多解,输出字典序最小的。
如果做不到,那么输出 “-1 -1”
(不加引号)
数据范围
字符串长度为 L 。
对于 30% 的数据: 1≤L≤10,1≤M≤100
对于 50% 的数据:除前面 30% 外, 1≤L≤500,M=5 或 25或 26
对于 100% 的数据: 1≤L≤2,000,1≤M≤200,000
样例输入
NETTLE
35
样例输出
1 2
样例解释
交换 N 和第一个 E 。
解题报告:
1:可以用一个数组将26的几次方存储。可以反着处理。
2:用O(n)的时间求出sum,后面每次交换字母,只需要将之前的贡献减掉,加上新的。具体可以看代码。
3:每次取模要使用sum = (sum%mod+mod)%mod,防止负数,爆数据范围对答案的影响。
4:代码中从0到 len-1处理的,输出答案时要将答案加上1。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2000+10;
ll mod, dic[N];
char ss[N];
int main(){
scanf("%s%lld", ss, &mod);
ll len = strlen(ss), x = 1, sum = 0;
for(ll i=len-1; i>=0; --i)
dic[i] = x, sum += x*(ss[i]-'A'), sum %= mod, x = x*26%mod;
if(!sum){
printf("0 0\n");
return 0;
}
for(ll i=0; i<len; ++i){
for(ll j=i+1; j<len; ++j){
ll ans = sum;//这里要特别注意=.=
ans = ans-dic[i]*(ss[i]-'A')-dic[j]*(ss[j]-'A');
ans = ans+dic[i]*(ss[j]-'A')+dic[j]*(ss[i]-'A'), ans = (ans%mod+mod)%mod;
if(!ans){
printf("%lld %lld\n", i+1, j+1);
return 0;
}
}
}
printf("-1 -1\n");
return 0;
}