数论杂题
URAL1095
题意:给定 \(t\) 个数字,每个数字不超过 \(20\) 位,并且每个数字的数码里都含有至少一组 \(1,2,3,4\)。对每个数字的数码进行重新排序,使得它能被 \(7\) 整除。
\(1 \le t \le 10000\)
分析:这题的关键在于“每个数字的数码里都含有至少一组 \(1,2,3,4\)”这个条件。这个条件这么奇怪,肯定是题目的突破口!可惜上课的时候没有注意繁杂的文字中这一句微小的条件。
注意到 \(4123 \% 7 = 0,1324 \% 7 = 1......\) 写一写发现这四个数字排列中可以把模 \(7\) 的所有余数取遍。那么我们把这些数字提出来,其他数字随便排,后面四位遍历一遍这七个四位数,如果可以整除就输出。因为放到后四位的时候这个数字相当于 \(XXXX...000y\),其中 \(y\) 就是这个四位数模 \(7\) 的余数(因为减掉若干个 \(7\) 之后与原来同余)。那么这七个数组成 \(7\) 的同余类,一定有一个整除 \(7\)。
注意不能有前导 \(0\),所以我们尽量从大到小排序其他的数码。其他数字都是 \(0\) 的时候直接输出 \(4123000...\) 即可。
#include<bits/stdc++.h>
using namespace std;
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef __int128_t lll;
typedef pair<int, int> pii;
const int inf = 1e9;
lll dem[] = {4123,1324,1234,2341,2314,2413,2134};
bool vis[5];
void output(lll x) {
string s;
while(x) {
s += char((x % 10) + '0');
x /= 10;
}
reverse(s.begin(), s.end());
cout << s << endl;
return;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
int t; cin >> t;
while(t--) {
memset(vis, 0, sizeof(vis));
string s; cin >> s;
lll ans = 0;
lll ch[25]; int cnt = 0;
f(i, 0, s.size() - 1) {
if(s[i]-'0'<5 && s[i]-'0'!=0 && !vis[s[i]-'0'])
vis[s[i]-'0']=1;
else ch[++cnt]=s[i]-'0';
}
sort(ch + 1, ch + cnt + 1, greater<lll>());
if(ch[1] == 0) {
cout << "4123";
f(i, 1, cnt) output(ch[i]);
cout << endl;
continue;
}
else {
f(i, 1, cnt) {ans *= 10; ans += ch[i];}
ans *= 10000;
f(j,0 , 6) if((ans + dem[j]) % 7 == 0) {
output(ans + dem[j]); break;
}
}
}
return 0;
}
ARC149A
居然被一道 A 题 AK 了。。
题意:给定 \(n,m\),求满足以下条件的最大数 \(X\):
- 位数在 \(n\) 以下。
- 能被 \(m\) 整除。
- 每一位的数码都相同。
\(n \le 10^5, m \le 10^9\)
分析:考虑枚举位数和数码,关键在于判断是否整除。
其实也很好判断,就是可以通过 \(i-1\) 位,数码都是 \(q\) 的数字模 \(m\) 的余数,递推出 \(i\) 位,数码都是 \(q\) 的数字模 \(m\) 的余数,只需要乘 \(10\) 加 \(q\) 即可。(模对于加法和乘法是有传递性的)然后就做完了。。。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
signed main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
time_t start = clock();
//think twice,code once.
//think once,debug forever.
int n ,m; cin >> n >> m;
int now = 0;
int maxi = 0, maxt = 0;
f(i, 1, n) {
now = (now * 10 + 1) % m;
f(j, 1, 9) {
if((now * j) % m == 0) {
maxi = i, maxt = j;
}
}
}
if(maxi == 0 ) cout << -1 << endl;
else {
f(i, 1, maxi) cout << maxt;
}
time_t finish = clock();
//cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
return 0;
}
预处理分解质因数
考虑 \(m\) 次询问,每次查询 \(a_i\) 的所有质因数。
\(m \le 10^6, a_i \le 10^7\)。
可以做一张表,每个数字不超过十个质因数。
埃筛可以做到 \(O(a_i \ln \ln a_i)\)。
但是其实我们只需要存储每个数字的最小质因数即可,这个线性筛可以做到。然后每次只需要跳转即可。