标签:href line 左移 扫描线 inline second code 多少 有序
题目链接:https://codeforces.com/contest/1029
题意:给一个长度为 \(n(1\leq n\leq50)\) 的字符串 \(t\) ,和一个正整数 \(k(1\leq k\leq50)\) ,要求构造一个最短的字符串 \(s\) ,使得 \(t\) 在 \(s\) 中恰好出现 \(k\) 次。
题解:看起来就像KMP算法,来个 \(O(n^3)\) 的做法就可以了,枚举重叠部分的两个开始位置,然后判断是否重叠,用此找出最长的重叠位置。但是直接复制一个前缀函数来用最简单。
在模板中提供的前缀函数是要求从0开始的,而 \(\pi(n)\) 即为整个字符串除去字符串本身的首尾重叠的最长长度。
注意留够空间。
int pi[3005];
void GetPrefixFunction(char *s, int sl) {
pi[0] = 0, pi[1] = 0;
for(int i = 1, k = 0; i < sl; ++i) {
while(k && s[i] != s[k])
k = pi[k];
pi[i + 1] = (s[i] == s[k]) ? ++k : 0;
}
}
char s[3005];
void test_case() {
int n, k;
scanf("%d%d%s", &n, &k, s);
GetPrefixFunction(s, n);
int p = pi[n];
int top = n;
--k;
while(k--) {
for(int i = 0; i < n - p; ++i) {
s[top] = s[top - (n - p)];
++top;
}
}
s[top] = '\0';
puts(s);
}
题意:给一个长度为 \(n(1\leq n \leq 2\cdot 10^5)\) 的严格单调递增的数列,要求从中选出若干个数,使得除了最大的那个数外,每个数都存在一个比它大且不超过它的两倍的数。
题解:很显然最容易满足“比某个数大且不超过它的两倍”的是它的下一个数,意思是一旦断掉就要重新开始咯。
int a[200005];
void test_case() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
int ans = 1, cur = 1;
for(int i = 2; i <= n; ++i) {
if(a[i] > a[i - 1] * 2)
cur = 1;
else {
++cur;
ans = max(ans, cur);
}
}
printf("%d\n", ans);
}
题意:给 \(n(2\leq n \leq 3\cdot 10^5)\) 条线段,移除恰好一条线段使得剩下的 \(n-1\) 条线段的交尽可能长。
题解:上次不是做过一个矩形版的吗?我还是用扫描线做的。这个直接维护线段的前缀交和后缀交就可以了。
int l[300005], r[300005];
int pl[300005], pr[300005];
int sl[300005], sr[300005];
void test_case() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d%d", &l[i], &r[i]);
pl[0] = -INF, pr[0] = INF;
for(int i = 1; i <= n; ++i) {
pl[i] = max(pl[i - 1], l[i]);
pr[i] = min(pr[i - 1], r[i]);
}
sl[n + 1] = -INF, sr[n + 1] = INF;
for(int i = n; i >= 1; --i) {
sl[i] = max(sl[i + 1], l[i]);
sr[i] = min(sr[i + 1], r[i]);
}
int ans = 0;
for(int i = 1; i <= n; ++i) {
int L = max(pl[i - 1], sl[i + 1]);
int R = min(pr[i - 1], sr[i + 1]);
//printf("L=%d R=%d\n", L, R);
ans = max(ans, R - L);
}
printf("%d\n", ans);
}
题意:给 \(n(2\leq n \leq 2\cdot 10^5)\) 个正整数和一个正整数 \(k\) ,求有多少个有序数对 \((i,j) (i\neq j)\) 使得第 \(i\) 个数后面接上第 \(j\) 个数之后被 \(k\) 整除。
题解:处理出每个数模 \(k\) 的余数,分长度存储在map里面。然后枚举第一个数和第二个数的长度(第一个数左移的量),就可以在对应的map里面查到有多少个数满足题意了。最后把 \((i,i)\) 的情况去掉。
注意:1e9接在1e9后面是大概1e19,会溢出。要考虑模运算对四则运算的等价变换。
int a[200005];
map<int, int> m[11];
int GetLen(int x) {
int len = 1;
while(x >= 10) {
x /= 10;
++len;
}
return len;
}
ll GetXX(int x, int len, int k) {
ll cur = 1;
while(len--)
cur *= 10;
cur += 1;
cur %= k;
cur *= x;
cur %= k;
return cur;
}
void test_case() {
int n, k;
scanf("%d%d", &n, &k);
ll ans = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
int len = GetLen(a[i]);
m[len][a[i] % k]++;
if(GetXX(a[i], len, k) == 0)
--ans;
}
for(int i = 1; i <= n; ++i) {
ll cur = a[i];
for(int j = 1; j <= 10; ++j) {
cur *= 10;
cur %= k;
int t = (k - cur) % k;
auto it = m[j].find(t);
if(it != m[j].end())
ans += (*it).second;
}
}
printf("%lld\n", ans);
}
Codeforces Round #506 (Div. 3)
标签:href line 左移 扫描线 inline second code 多少 有序
原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12244534.html