暑假N天乐【比赛篇】 —— 2019杭电暑期多校训练营(第二场)
这段时间自己都不知道该干些什么,比赛时候什么都想不到,全靠队友A题维持生计这样...补题进度也是一拖再拖,明后天又是两场连打,感觉又要堆一堆题了...看着补算了。
以下题解包括:
【1005】 数学 HDU-6595 Everything Is Generated In Equal Probability
与我无瓜,比赛时候队友A的。据说是找规律
http://acm.hdu.edu.cn/showproblem.php?pid=6595
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
ll q_pow(ll a, ll b) {
ll ans = 1;
while(b) {
if(b & 1) {
ans = ans * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return ans;
}
int main() {
int n;
while(~scanf("%d", &n)) {
printf("%lld\n", (1ll*n*n-1)%mod*q_pow(9ll, mod-2)%mod);
}
return 0;
}
【1009】 回文自动机 HDU-6599 I Love Palindrome String
我不爱回文串,听他们说是回文自动机板子,然后我就去找板子了,然后一同乱改,然后A了。
http://acm.hdu.edu.cn/showproblem.php?pid=6599
回文自动机的大佬讲解和板子:https://blog.csdn.net/weixin_43093481/article/details/82889017
本题参考题解:https://www.cnblogs.com/xay5421/p/HDU6599.html
求有多少个 \([l,r]\) 满足 \(s[l,r]\) 和 \(s[l,(l+r)/2]\) 都是回文串。
考虑本质不同的回文串总共有 \(O(n)\) 种,然后统计每个满足条件的回文串出现次数和。
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 3e5+5;
const int N = 26;
const int seed = 131;
char a[maxn];
ull hash1[maxn], hash2[maxn], pw[maxn];
int ans[maxn];
ull get(int l, int r) {
return hash1[r] - hash1[l-1]*pw[r-l+1];
}
ull get2(int l, int r) {
return hash2[l] - hash2[r+1]*pw[r-l+1];
}
bool check(int l, int r){
return get(l,r) == get2(l,r) && get(l,(l+r)>>1) == get2(l,(l+r)>>1);
}
struct Palindromic_Tree {
int nxt[maxn][N]; // next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[maxn]; // fail指针,失配后跳转到fail指针指向的节点
ll cnt[maxn]; // 表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)
int num[maxn]; // 表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
int len[maxn]; // len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
int S[maxn]; // 存放添加的字符
int last; // 指向新添加一个字母后所形成的最长回文串表示的节点。
int n; // 表示添加的字符个数。
int p; // 表示添加的节点个数。
int ok[maxn]; // 判断是否可行
int newnode(int l, int r) {// 新建节点
for(int i = 0; i < N; ++i)
nxt[p][i] = 0;
cnt[p] = 0;
num[p] = 0;
len[p] = l;
fail[p] = r;
return p++;
}
void init() { // 初始化
p = 0;
newnode(0, 1);
newnode(-1, 0);
last = 0;
n = 0;
S[n] = -1; // 开头放一个字符集中没有的字符,减少特判
fail[0] = 1;
}
int get_fail(int x, int y) { // 和KMP一样,失配后找一个尽量最长的
while(a[y-len[x]-1] != a[y]) x = fail[x];
return x;
}
void add(int c, int pos) {
c -= 'a';
int cur = get_fail(last, pos); // 通过上一个回文串找这个回文串的匹配位置
if(!nxt[cur][c]) { // 如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
int now = newnode(len[cur]+2, nxt[get_fail(fail[cur],pos)][c]); // 新建节点
//fail[now] = nxt[get_fail(fail[cur], pos)][c]; // 和AC自动机一样建立fail指针,以便失配后跳转
nxt[cur][c] = now;
//num[now] = num[fail[now]] + 1;
ok[nxt[cur][c]] = check(pos-len[nxt[cur][c]]+1, pos);
}
last = nxt[cur][c];
cnt[last] ++;
}
void count() {
for(int i = p-1; i >= 2; --i) {
// 父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
cnt[fail[i]] += cnt[i];
}
for(int i = p-1; i >= 2; --i){
ans[len[i]] += ok[i] * cnt[i];
}
}
}T;
int main() {
pw[0] = 1;
for(int i = 1; i < maxn; i++) {
pw[i] = pw[i-1]*seed;
}
while(~scanf("%s", a+1)) {
T.init();
int n = strlen(a+1);
hash1[0] = 0;
hash2[n+1] = 0;
for(int i = 1; i <= n; i++) {
hash1[i] = hash1[i-1]*seed + a[i]-'a'+1;
}
for(int i = n; i >= 1; i--) {
hash2[i] = hash2[i+1]*seed + a[i]-'a'+1;
}
for(int i = 1; i <= n; i++) {
T.add(a[i], i);
ans[i] = 0;
}
T.count();
for(int i = 1; i <= n; i++) {
printf("%d%c", ans[i], i==n ? '\n':' ');
}
}
return 0;
}
【1010】 水题 HDU-6600 Just Skip The Problem
就是找规律,发现答案就是 n!,然后 %(1e6+3),意味着大于 (1e6+3) 的答案都是 0。
http://acm.hdu.edu.cn/showproblem.php?pid=6600
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e6 + 3;
const int maxn = 1e6+3;
ll a[maxn+5];
int main() {
int n;
a[0] = 1;
for(int i = 1; i <= maxn; i++) {
a[i] = a[i-1]*i % mod;
}
while(~scanf("%d", &n)) {
if(n >= maxn) {
printf("0\n");
}
else {
printf("%lld\n", a[n]);
}
}
return 0;
}
【1011】 斐波那契+主席树 HDU-6601 Keen On Everything But Triangle
比赛的时候肝了4个小时...从WA到TLE到WA到MLE再到WA,莫队被我疯狂魔改,每次都能被找出反例,于是我心态炸了。
http://acm.hdu.edu.cn/showproblem.php?pid=6601
给定 n 个数和 m 次询问,每次给定区间 \([l, r]\),要找到给定区间内最大的三角形。
因为斐波那契数的存在,我们可以知道:如果区间内一直不能构成三角形,那么最多也只会出现40多次这样的情况,因为 \(fi_{40+}\) 就已经会超过上界 \(1e^9\),因此我们对于每一个区间,只需要用主席树每次去处理区间前3大,依次往后找就可以了,这样的时间总时间复杂度就是:\(o(m*log(n)*40)\)。
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e5+5;
int n, q, cnt;
int a[maxn], root[maxn];
vector<int> v;
struct node {
int l, r, sum;
} T[maxn*40];
int getid(int x) {
return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
void update(int l, int r, int &x, int y, int pos) {
T[++cnt] = T[y];
T[cnt].sum ++;
x = cnt;
if(l == r) {
return ;
}
int mid = (l+r) / 2;
if(mid >= pos) {
update(l, mid, T[x].l, T[y].l, pos);
}
else {
update(mid+1, r, T[x].r, T[y].r, pos);
}
}
int query(int l, int r, int x, int y, int k) {
if(l == r) {
return l;
}
int mid = (l+r) / 2;
int sum = T[T[y].l].sum - T[T[x].l].sum;
if(sum >= k) {
return query(l, mid, T[x].l, T[y].l, k);
}
else {
return query(mid+1, r, T[x].r, T[y].r, k-sum);
}
}
int main() {
while(~scanf("%d%d", &n, &q)) {
memset(root, 0, sizeof(root));
memset(a, 0, sizeof(a));
v.clear();
cnt = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
v.push_back(a[i]);
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
int new_n = (int)v.size();
for(int i = 1; i <= n; i++) {
update(1, new_n, root[i], root[i-1], getid(a[i]));
}
for(int i = 1 ; i <= q; i++) {
int l, r;
scanf("%d%d", &l, &r);
int s = r - l + 1;
if(s <= 2) {
printf("-1\n");
continue;
}
int a = v[query(1, new_n, root[l-1], root[r], s) - 1];
int b = v[query(1, new_n, root[l-1], root[r], s-1) - 1];
int c = v[query(1, new_n, root[l-1], root[r], s-2) - 1];
if(a < b+c) {
printf("%lld\n", 1ll*a+b+c);
}
else {
int flag = 0;
for(int i = 3; i <= s-1; i++) {
a = b;
b = c;
c = v[query(1, new_n, root[l-1], root[r], s-i) - 1];
if(a < b+c) {
flag = 1;
break;
}
}
if(flag == 0) {
printf("-1\n");
}
else {
printf("%lld\n", 1ll*a+b+c);
}
}
}
}
return 0;
}
【1012】 线段树+双端队列 HDU-6602 Longest Subarray
http://acm.hdu.edu.cn/showproblem.php?pid=6602
给你一个数组,数的范围是 \([1,C]\),给定 \(K\),让你找一个最长的区间使得区间内任意一个出现的数在该区间内的数量都大于 \(K\) 或者等于 0。
参考博客:https://blog.csdn.net/qq_40791842/article/details/97434430
整体思想就是枚举右端点,找最左的左端点。考虑建一颗线段树,每个叶子节点记录一改位置为左节点,当前枚举的节点为右节点的合法颜色种类数量,显然如果等于 C,那么以这个节点为左节点是合法的,由于线段树叶子节点最大权值为 C,所以可以通过维护一个最大值,和最大值对应的最左边的那个位置,查询这个位置就是最优左区间位置。
还需要用双端队列 \(deque\) 维护每个数的位置信息。
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e5+5;
int n, c, k;
int a[maxn];
int sum[maxn<<2];
int pos[maxn<<2];
int lazy[maxn<<2];
deque<int> q[maxn]; // q[i]维护数 i 的最多 k 个最新位置信息
void init() {
for(int i = 0; i <= c; i++) {
q[i].clear();
q[i].push_back(0);
}
}
void push_up(int rt) {
if(sum[rt<<1] >= sum[rt<<1|1]) {
sum[rt] = sum[rt<<1];
pos[rt] = pos[rt<<1];
}
else {
sum[rt] = sum[rt<<1|1];
pos[rt] = pos[rt<<1|1];
}
}
void push_down(int rt) {
if(lazy[rt]) {
sum[rt<<1] += lazy[rt];
sum[rt<<1|1] += lazy[rt];
lazy[rt<<1] += lazy[rt];
lazy[rt<<1|1] += lazy[rt];
lazy[rt] = 0;
}
}
void build(int l, int r, int rt) {
sum[rt] = lazy[rt] = 0;
pos[rt] = l;
if(l == r) {
return ;
}
int mid = (l+r) >> 1;
build(l, mid, rt<<1);
build(mid+1, r, rt<<1|1);
}
void update(int L, int R, int l, int r, int rt, int x) {
if(L <= l && r <= R) {
sum[rt] += x;
lazy[rt] += x;
return ;
}
push_down(rt);
int mid = (l+r) >> 1;
if(L <= mid) {
update(L, R, l, mid, rt<<1, x);
}
if(R > mid) {
update(L, R, mid+1, r, rt<<1|1, x);
}
push_up(rt);
}
pair<int, int> query(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) {
pair<int, int> temp;
temp.first = sum[rt];
temp.second = pos[rt];
return temp;
}
push_down(rt);
int mid = (l+r) >> 1;
pair<int, int> ansl, ansr;
if(L <= mid) {
ansl = query(L, R, l, mid, rt<<1);
}
if(R > mid) {
ansr = query(L, R, mid+1, r, rt<<1|1);
}
if(ansl.first >= ansr.first) {
return ansl;
}
return ansr;
}
int solve(int l, int r) {
auto it = query(l, r, 1, n, 1);
if(it.first != c) {
return -1;
}
return it.second;
}
int main() {
while(~scanf("%d%d%d", &n, &c, &k)) {
init();
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
if(k <= 1) {
printf("%d\n", n);
continue;
}
build(1, n, 1);
int ans = 0;
for(int i = 1; i <= n; i++) {
int lst = q[a[i]].back();
if(lst+1 <= i-1) {
update(lst+1, i-1, 1, n, 1, -1);
}
if(q[a[i]].size() == k) {
int fst = q[a[i]].front();
q[a[i]].pop_front();
int nxt = q[a[i]].front();
update(fst+1, nxt, 1, n, 1, 1);
}
q[a[i]].push_back(i);
update(i, i, 1, n, 1, c-1);
int q = solve(1, i);
if(q != -1) {
ans = max(ans, i-q+1);
}
}
printf("%d\n", ans);
}
return 0;
}