牛客多校第四场自闭
自闭场,不想多说,能做的就2个签到和H了,难度2.0就是字典树上后缀自动机,知识储备限制了我的想象
H-Harder Gcd Problem
先挂个线性筛把范围内素数都筛一下,然后直接lowerbound找到第一个小于n的素数位置(实际上至少要小于等于n/2,我懒,直接lowerbound到n了),然后从大往小依次处理
对于每一个素数,让\(j=(n/p[i])*p[i]\)(即找到最后一个j使得jp[i]<=n),然后把j向1枚举,每两个jp[i]组成一对答案,但对于j为奇数的情况,2*p[i]得先暂时保留
最后再遍历没被匹配的数,这时刚刚2*p[i]中带有2的因子,因此他们也能够相互匹配,这样再把它们两两匹配
我的处理是先让1*p[i]处于待匹配状态,若j为偶数,则2也能被匹配;若j为奇数,则2只能进入待匹配状态,而不会被匹配
#pragma GCC optimize(2)
#include
#define ll long long
#define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
double pi = acos(-1);
const double eps = 1e-12;
const int maxn = 4e5 + 10;
const int inf = 1e9;
ll mod = 1e9 + 7;
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
return f * x;
}
int v[maxn], p[maxn], ecnt, vis[maxn], cnt = 0;
void Euler_sieve(int n)
{
ecnt = 0;
for (int i = 2; i <= n; i++) {
if (v[i] == 0) { v[i] = i; p[++ecnt] = i; }
for (int j = 1; j <= ecnt; j++)
{
if (p[j] > v[i] || p[j] > n / i)break;
v[i * p[j]] = p[j];
}
}
}
struct an {
int a, b;
}ans[maxn];
int main()
{
fastio;
int t;
cin >> t;
Euler_sieve(3e5 + 5);
while(t--)
{
memset(vis, 0, sizeof(vis));
int n;
cin >> n;
cnt = 0;
int P = lower_bound(p + 1, p + 1 + ecnt, n) - p - 1;
int tmp = -1;
for (int i = P; i >= 1; i--)
{
tmp = p[i];
for (int j = n / p[i] * p[i]; j > p[i]; j -= p[i])
{
if (!vis[j])
{
if (tmp == -1)
tmp = j;
else
{
ans[++cnt] = { tmp,j };
vis[tmp] = vis[j] = 1;
tmp = -1;
}
}
}
}
tmp = -1;
for (int i = 2; i <= n; i += 2)
{
if (!vis[i])
if (tmp == -1)
tmp = i;
else
{
ans[++cnt] = { tmp,i };
vis[tmp] = vis[i] = 1;
tmp = -1;
}
}
cout << cnt << endl;
for (int i = 1; i <= cnt; i++)
cout << ans[i].a << " " << ans[i].b << endl;
}
return 0;
}
D-Dividing Strings
阴间字符串,模拟+思维难度感觉很大
首先需要知道的是:函数传入大字符串会T,建议直接取地址,具体差多少时间,见图:
剩下的就是做法了,打的时候想dp想半天没整出来,结果是模拟,我直接爬
由样例可得到的结论:
拆成长度为1的n个数,ans肯定<=9,因此枚举长度只有两种(1、拆成长度相同的串;2、拆成长度为d和d-1的串;3否则不能使ans<=9)
对于长度相同的串,直接找到最大值和最小值比较就行了
对于长度相差1的串:当且仅当长度为d的串为100000000x,长度为d-1的串为99999999y时相减<=9(反证法:出现其他串时MAX和MIN都可能改变,相减必大于9)
当d==2时,需要特判1x和y,让MAX=1x,MIN=y(我的代码好像不需要处理这个就能ac,个人写法可能有影响)
一定要记得把有前导零的串给筛掉(指相同长度的串匹配)
以下是代码(感谢ac代码帮模拟手残人找出错误)
#pragma GCC optimize(2)
#include
#define ll long long
using namespace std;
double pi = acos(-1);
const double eps = 1e-12;
const int maxn = 1e5 + 10;
const int inf = 1e9;
string s;
int ans;
int pre[maxn];
int dif(string &a, string &b)
{
int len = a.length();
int res = 0;
for (int i = 0; i < len; i++)
{
res = res * 10 + a[i] - b[i];
if (res >= 9)return 9;
}
return res;
}
void div1(string &s,int d)//一定要取地址,不然无限TLE,下同
{
string MAX = "0", MIN = "z";
string tmp;
int len = s.length();
if (d > len / 2)return;
int i = 0;
while (i < len)
{
tmp = s.substr(i, d);
if (tmp[0] == '0' && d > 1)return;//筛掉前导零
MAX = max(tmp, MAX);//stl自带的字典序比较,我直接觉得牛逼
MIN = min(tmp, MIN);
i += d;
}
ans = min(ans, dif(MAX, MIN));
}
void div2(string &s, int d)
{
int len = s.length();
int i = 0, MAX = -1, MIN = 10;
if (d == 1)return;
if (len - d < d - 1)return;
while (i < len)//必然只能是长度相差1的1xxxxxxxy和9999999z,出现其他数的话1xxx就可能不是最大,9999..不是最小
{
if (s[i] == '1')
{
if (i + d > len)return;
if (pre[i + d - 1] - pre[i + 1])return;
MAX = max(MAX, s[i + d - 1] - '0');
i += d;
}
else
{
if (i + d - 1 > len)return;
if (pre[i + d - 2] - pre[i] != (d - 2) * 9)
return;
MIN = min(MIN, s[i + d - 2] - '0');
i += d - 1;
}
}
//cout << MAX << " " << MIN << endl;
if (MAX == -1 || MIN == 10)return;
ans = min(ans, 10 - MIN + MAX);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
ans = 9;
cin >> s;
int len = s.length();
pre[0] = 0;
for (int i = 1; i <= len; i++)
pre[i] = pre[i - 1] + s[i - 1] - '0';
for (int i = 1; i <= len / 2 + 1; i++)
{
if (len % i == 0)
div1(s, i);
div2(s, i);
// cout << ans << endl;
}
printf("%d\n", ans);
}
return 0;
}