您的位置:首页 > 大数据 > 人工智能

2016 Multi-University Training Contest 4

2016-07-31 15:24 597 查看
天天不知道忙些什么,懒得博客都不贴了。

其实我真的想花大量时间好好学英语。

A

题意:给定两个串a和b,你可以选择在串a中把与b相同的子串替换成∗。问你可以得到多少种不同的新串。

思路:先kmp,记录所有与b相等的子串的区间[l,r],然后做dp。

dp[i][0]表示处理前i个且第i个区间不选用。

dp[i][1]表示处理前i个且选用第i个区间。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <string>
#define CLR(a, b) memset(a, (b), sizeof(a))
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 10;
const int MAXM = 1e5 + 1;
const int INF = 1e9 + 10;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) {x += y; x %= MOD;}
int s[MAXN];
LL dp[MAXN][2];
int len1, len2, top;
char s1[MAXN], s2[MAXN];
int nextn[MAXN];
void makenext() {
int j = -1, i = 0; nextn[i] = j;
while(i < len1) {
if(j == -1 || s1[i] == s1[j]) {
i++; j++;
nextn[i] = j;
}
else {
j = nextn[j];
}
}
}
void kmp() {
int i = 0, j = 0;
makenext();
while(i < len2) {
if(j == -1 || s2[i] == s1[j]) {
i++; j++;
if(j == len1) {
s[++top] = i-len1;
}
}
else {
j = nextn[j];
}
}
}
int main()
{
int t, kcase = 1; scanf("%d", &t);
while(t--) {
scanf("%s%s", s2, s1);
len1 = strlen(s1); len2 = strlen(s2); top = 0; kmp();
CLR(dp, 0); dp[0][0] = 1LL; dp[0][1] = 0;
for(int i = 1; i <= top; i++) {
if(i == 1) {
dp[i][0] = dp[i][1] = 1LL;
}
else {
add(dp[i][1], dp[i-1][0] + dp[i-1][1]);
if(s[i] - s[i-1] + 1 <= len1) {
int j = lower_bound(s + 1, s + i - 1, s[i] - len1 + 1) - s - 1;
add(dp[i][0], dp[j][0] + dp[j][1]);
}
else {
add(dp[i][0], dp[i-1][0] + dp[i-1][1]);
}
}
}
LL ans = 0;
add(ans, dp[top][0] + dp[top][1]);
printf("Case #%d: %lld\n", kcase++, ans);
}
return 0;
}


E

题意:给定n个a[i],p[i],问你在区间[l,r]里面可以被7整除的数里面有多少个满足对任意一个i均有%a[i]!=p[i]。

MDZZ,过了后缀数组那道,看看过题数,瞬间感觉被欺骗了。

思路:这就是一个很裸的斥 + CRT的题目。

我们做过类似 给你n个数,问区间[l,r]有多少个数满足不被这n个数任意一个整除,这和上面是一样的,只不过多了个被7整除。我们每次容斥的时候把%7==0加上,CRT的时候会爆long long,中间套个二进制优化乘法即可。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <string>
#define CLR(a, b) memset(a, (b), sizeof(a))
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 10;
const int MAXM = 1e5 + 1;
const int INF = 1e9 + 10;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) {x += y; x %= MOD;}
LL multi(LL a, LL b, LL m){
LL ans = 0;
while(b) {
if(b & 1LL) {
ans = (ans + a) % m;
}
a = (a << 1) % m;
b >>= 1;
}
return ans;
}
LL gcd(LL a, LL b) {
return b == 0 ? a : gcd(b, a % b);
}
void exgcd(LL a, LL b, LL &d, LL &x, LL &y)
{
if(b == 0) {d = a, x = 1, y = 0;}
else {
exgcd(b, a % b, d, y, x);
y -= x * (a / b);
}
}
LL M;
LL CRT(LL l, LL r, LL *m, LL *a) {
M = 1;
LL d, y, x = 0;
for(LL i = l; i <= r; i++) {
M = M / gcd(M, m[i]) * m[i];
}
for(LL i = l; i <= r; i++) {
LL w = M / m[i];
exgcd(m[i], w, d, d, y);
x = (x + multi(multi(y, w, M), a[i], M)) % M;
}
return (x + M) % M;
}
LL Count(LL ans, LL N) {
if(ans > N) return 0;
else {
return (N - ans) / M + 1;
}
}
LL a[20], p[20];
LL x[20], y[20], k;
int main()
{
int t, kcase = 1; scanf("%d", &t);
while(t--) {
int n; LL l, r; scanf("%d%lld%lld", &n, &l, &r);
for(int i = 0; i < n; i++) {
scanf("%lld%lld", &a[i], &p[i]);
}
x[0] = 7, y[0] = 0;
LL ans = 0;
for(int i = 0; i < (1 << n); i++) {
int cnt = 0; k = 1;
for(int j = 0; j < n; j++) {
if(i & (1 << j)) {
x[k] = a[j];
y[k++] = p[j];
cnt++;
}
}
LL temp = CRT(0, k - 1, x, y);
LL res = Count(temp, r) - Count(temp, l - 1);
if(cnt & 1) {
ans -= res;
}
else {
ans += res;
}
}
printf("Case #%d: %lld\n", kcase++, ans);
}
return 0;
}


F

题意:问你包含字符x的不同子串个数。

一开始想歪了,MDZZ。其实是一道裸后缀数组,我们二分找到距离每个后缀最近的x字符的位置,然后统计每个后缀的贡献即可。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <string>
#define CLR(a, b) memset(a, (b), sizeof(a))
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 10;
const int MAXM = 1e5 + 1;
const int INF = 1e9 + 10;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) {x += y; x %= MOD;}
int cmp(int *r, int a, int b, int l) {
return (r[a] == r[b]) && (r[a+l] == r[b+l]);
}
int wa[MAXN], wb[MAXN], ws[MAXN], wv[MAXN];
int R[MAXN];
int H[MAXN];
void DA(int *r, int *sa, int n, int m) {
int i, j, p, *x = wa, *y = wb, *t;
for(i = 0; i < m; i++) ws[i] = 0;
for(i = 0; i < n; i++) ws[x[i]=r[i]]++;
for(i = 1; i < m; i++) ws[i] += ws[i-1];
for(i = n-1; i >= 0; i--) sa[--ws[x[i]]] = i;
for(j = 1, p = 1; p < n; j *= 2, m = p) {
for(p = 0, i = n-j; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j;
for(i = 0; i < n; i++) wv[i] = x[y[i]];
for(i = 0; i < m; i++) ws[i] = 0;
for(i = 0; i < n; i++) ws[wv[i]]++;
for(i = 1; i < m; i++) ws[i] += ws[i-1];
for(i = n-1; i >= 0; i--) sa[--ws[wv[i]]] = y[i];
for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i++)
x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
}
}
void calh(int *r, int *sa, int n) {
int i, j, k = 0;
for(i = 1; i <= n; i++) R[sa[i]] = i;
for(i = 0; i < n; H[R[i++]] = k)
for(k ? k-- : 0, j = sa[R[i]-1]; r[i+k] == r[j+k]; k++);
}
int sa[MAXN];
int a[MAXN], id[MAXN];
char str[MAXN];
vector<int> pos;
int main()
{
int t, kcase = 1; scanf("%d", &t);
while(t--) {
char op[2];
scanf("%s%s", op, str);
int n = strlen(str); int Max = 0; pos.clear();
for(int i = 0; i < n; i++) {
a[i] = str[i]; Max = max(Max, a[i]);
if(str[i] == op[0]) {
pos.push_back(i);
}
}
a
= 0; DA(a, sa, n+1, Max+1); calh(a, sa, n);
for(int i = 0; i < n; i++) {
int p = lower_bound(pos.begin(), pos.end(), i) - pos.begin();
if(p == pos.size()) {
id[i] = -1;
}
else {
id[i] = pos[p];
}
}
LL ans = 0;
for(int i = 1; i <= n; i++) {
if(id[sa[i]] == -1) continue;
if(i == 1) ans += n - id[sa[i]];
else {
ans += n - max(H[i] + sa[i], id[sa[i]]);
}
}
printf("Case #%d: %lld\n", kcase++, ans);
}
return 0;
}


J

题意:你可以把元素中的0改成任意整数,问你可以得到的LIS。

思路:0全部用上一定最优,那考虑0的个数对非0元素处理一下,跑一发LIS。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <string>
#define CLR(a, b) memset(a, (b), sizeof(a))
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 10;
const int MAXM = 1e5 + 1;
const int INF = 1e9 + 10;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) {x += y; x %= MOD;}
int g[MAXN], a[MAXN], b[MAXN], dp[MAXN];
int pre[MAXN];
int main()
{
int t, kcase = 1; scanf("%d", &t);
while(t--) {
int n; scanf("%d", &n);
int top = 0; int cnt = 0, sum = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if(a[i] != 0) {
pre[++top] = cnt; b[top] = a[i];
g[top] = INF; dp[top] = 1; cnt = 0;
}
else {
sum++; cnt++;
}
}
int ans = 0;
for(int i = 1; i <= top; i++) {
int k = lower_bound(g + 1, g + top + 1, b[i] - pre[i]) - g;
dp[i] = k; ans = max(ans, dp[i]);
g[dp[i]] = min(g[dp[i]], b[i] - pre[i]);
}
printf("Case #%d: %d\n", kcase++, ans + sum);
}
return 0;
}


K

求字符串出现次数,不说了。

L

题意:问你冒泡排序过程中元素出现的最左位置和最右位置的绝对值。

思路:发现若i后有num比它小的元素,那么它一定会向右移动num个位置。之后肯定是回到i位置了。

最右位置r=pos[i]+num(比它小的元素个数)。

最左位置 = min(pos[i],i)。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <string>
#define CLR(a, b) memset(a, (b), sizeof(a))
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 10;
const int MAXM = 1e5 + 1;
const int INF = 1e9 + 10;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) {x += y; x %= MOD;}
pii a[MAXN];
int lowbit(int x) {
return x & (-x);
}
int n;
int C[MAXN];
void add(int x) {
while(x <= n) {
C[x]++;
x += lowbit(x);
}
}
int Sum(int x) {
int s = 0;
while(x > 0) {
s += C[x];
x -= lowbit(x);
}
return s;
}
int num[MAXN], pos[MAXN];
int main()
{
int t, kcase = 1; scanf("%d", &t);
while(t--) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i].first);
a[i].second = i; pos[a[i].first] = i;
}
sort(a + 1, a + n + 1); CLR(C, 0);
for(int i = 1; i <= n; i++) {
num[a[i].first] = Sum(n) - Sum(a[i].second);
add(a[i].second);
}
printf("Case #%d:", kcase++);
for(int i = 1; i <= n; i++) {
//cout << num[i] << endl;
int r = pos[i] + num[i];
int l = min(i, pos[i]);
printf(" %d", r - l);
}
printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: