首页 > 其他分享 >蔚来杯2022牛客暑期多校训练营10 题解

蔚来杯2022牛客暑期多校训练营10 题解

时间:2022-08-25 21:59:52浏览次数:120  
标签:10 lcp int 蔚来 pos 枚举 MAXN 题解 define

D. Mi Re Do Si La? So Fa!

[NOI2016] 优秀的拆分 原题。

枚举周期 \(k\), 并将位置为 \(k\) 的倍数的点设为关键点。枚举相邻两个点 \(i,i+k\),并求出 \(lcp(S[i...n],S[i+k...n])\) 和 \(lcs(S[1...i],S[1...i+k])\),其中 \(lcp\) 为最长前缀,\(lcs\) 为最长后缀。我们需要求出以 \([i,i+k)\) 内的点为终点的周期为 \(k(k != |T|)\) 的所有子串 \(T\) 数量,所以将 \(lcp=min(lcp,k)\)。

得子串 \(T\) 数量为 \(lcp \cdot \lfloor \frac{lcs}{k} \rfloor+max(0,lcp+lcs\%k+1-k)\)。

再加上 \(k=|T|\) 的情况,有 \(n-k+1\) 个。

即可得出长度为 \(k\) 时有多少个子串满足条件。

复杂度为调和级数 \(O(nlogn)\)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1e6 + 7;

int oldrk[MAXN * 2], cnt[MAXN], id[MAXN], tmp[MAXN], n, m;
char S[MAXN];
int rk[2][MAXN * 2], sa[2][MAXN * 2], height[2][MAXN][21];

void SuffixArray(int pos, int m = 26) {
	for(int i = 0; i <= n; ++i) height[pos][i][0] = 0;
	for(int i = 0; i <= 2 * n; ++i) rk[pos][i] = sa[pos][i] = oldrk[i] = 0;
	for(int i = 0; i <= m; ++i) cnt[i] = 0;
	
	for(int i = 1; i <= n; i++) cnt[ rk[pos][i] = S[i] ]++;
	for(int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
	for(int i = n; i; i--) sa[pos][ cnt[ S[i] ]-- ] = i;
	
	int len = 1, maxx = 0;
	while(maxx < n) {
		int k = 0;
		for(int i = n - len + 1; i <= n; i++) id[++k] = i;
		for(int i = 1; i <= n; i++) if(sa[pos][i] > len) id[++k] = sa[pos][i] - len;
		for(int i = 0; i <= m; i++) cnt[i] = 0;
		for(int i = 1; i <= n; i++) cnt[ tmp[i] = rk[pos][ id[i] ] ]++;
		for(int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
		for(int i = n; i; i--) sa[pos][ cnt[ tmp[i] ]-- ] = id[i];
		for(int i = 1; i <= n; i++) oldrk[i] = rk[pos][i];
		int p = 1; rk[pos][ sa[pos][1] ] = 1;
		for(int i = 2; i <= n; i++) {
			if(oldrk[ sa[pos][i] ] != oldrk[ sa[pos][i - 1] ] || oldrk[ sa[pos][i] + len ] != oldrk[ sa[pos][i - 1] + len ]) p++;
			rk[pos][ sa[pos][i] ] = p;
		}
		len <<= 1; maxx = m = p;
	}
	
	for(int i = 1, k = 0; i <= n; ++i) {
		if(k) --k;
		while(S[i + k] == S[ sa[pos][ rk[pos][i] - 1 ] + k ]) ++k;
		height[pos][ rk[pos][i] ][0] = k;
	}
	
	//for(int i = 1; i <= n; ++i) printf("%d ", sa[i]); printf("sa\n");
	//for(int i = 1; i <= n; ++i) printf("%d ", rk[i]); printf("rk\n");
	//for(int i = 1; i <= n; ++i) printf("%d ", height[i][0]); printf("height\n");

	for(int k = 1; (1 << k) <= n; ++k) {
		for(int i = 1; i + (1 << k) - 1 <= n; ++i) height[pos][i][k] = min(height[pos][i][k - 1], height[pos][i + (1 << k - 1)][k - 1]);
	}
}

int QueryLCP(int pos, int l, int r) {
	if(l < 1 || r < 1 || l > n || r > n) return 0;
	l = rk[pos][l], r = rk[pos][r];
	if(l == r) return n + 1 - l;
	if(l > r) swap(l, r);
	l++;
	int t = floor( log2(r - l + 1) );
	return min(height[pos][l][t], height[pos][r - (1 << t) + 1][t]);
}

signed main()
{
	//ios::sync_with_stdio(false); cin.tie(0);
	int T; scanf("%d", &T);
	long long res = 0, cnt = 0;
	int l, r, lcp, lcs;	
	while(T--) {
		scanf("%s", S + 1); n = strlen(S + 1); //m = max(n, 300);
		for(int i = 1; i <= n; i++) S[i] = S[i] - 'a' + 1;
		SuffixArray(0);	//LCP
		reverse(S + 1, S + n + 1);
		SuffixArray(1);	//LCS
		res = 0, cnt = 0;
		for(int len = 1; len <= n; ++len) res += (long long)(n - len + 1) * len;
		for(int len = 1; len <= n / 2; ++len) {
			cnt = 0;
			for(int i = len; i + len <= n; i += len) {
				l = i, r = i + len;
				lcp = min(len, QueryLCP(0, l, r) );
				lcs = QueryLCP(1, n + 1 - (l - 1), n + 1 - (r - 1) );
				cnt += lcp * (lcs / len);
				lcs %= len;
				if(lcp + lcs >= len) cnt += (lcp + lcs - len + 1);
			}
			res += cnt * len;
		}
		printf("%lld\n", res);
	}
	return 0;
}

F. Shannon Switching Game?

设 \(st_i\) 表示先手必胜还是必败。这个游戏我们最多进行 \(O(n)\) 轮,且 \(O(n)\) 轮中每轮至少更新一个点,若没有更新点则直接退出判断结果。

先将终点设为必胜态,对每轮游戏中,对于每个点 \(u\),若有至少两条出边 \(u->v\) 满足 \(st_v\) 为必胜态,则可以将 \(u\) 纳入必胜态。

最后判断起点是不是必胜态即可。

#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;

const int MAXN = 105;
const int MAXM = 1e4 + 5;

int n, m, s, t, st[MAXN];
map<int, int> G[MAXN];

void Solve() {
	cin >> n >> m >> s >> t;
	for(int i = 1; i <= n; i++) G[i].clear();
	for(int i = 1; i <= m; i++) {
		int u, v; cin >> u >> v;
		G[u][v]++;
        G[v][u]++;
	}
	for(int i = 1; i <= n; i++) st[i] = 0;
	st[t] = 1;
	for(int k = 1; k <= n; k++) {
		int cnt;
		for(int i = 1; i <= n; i++) {
			cnt = 0;
			for(auto &j : G[i]) if(st[j.fi]) cnt += j.se;
			if(cnt >= 2) st[i] = 1;
		}
	}
	if(st[s]) cout << "Join Player\n";
	else cout << "Cut Player\n";
}

signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T;
	while(T--) Solve();
	return 0;
}

I. Yet Another FFT Problem?

如果 \(a,b\) 各有一对相同数字可直接输出方案。

除去这个情况后,我们可以对于 \(a,b\) 去重并排序,这样的情况是两数组中数字两两不相同且条件中绝对值可去掉。

移项,有 \(a_i+b_l=a_j+b_k\)。我们可以直接用两层循环枚举 \(sum=a_i+b_l\),枚举过程中发现先前被枚举到的 \(sum\) 和当前 \(sum\) 相同可直接输出方案。

对于 \(-1\) 的情况,发现 \(a_i+b_l\) 的上界为 \(2e7\),则最多只会有 \(2e7\) 个值被枚举到,接下来无论枚举到哪个值,都可以根据抽屉原理得出合法方案。

#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;

const int MAXN = 1e6 + 5;
const int MAXM = 2e7 + 5;

int n, m, c[MAXM];
pii a[MAXN], b[MAXN], pos[MAXM];

signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> n >> m;
	for(int i = 1; i <= n; i++) cin >> a[i].fi, a[i].se = i;
	for(int i = 1; i <= m; i++) cin >> b[i].fi, b[i].se = i;
	sort(a + 1, a + 1 + n);
	sort(b + 1, b + 1 + m);
	int pos1 = 0, pos2 = 0;
	for(int i = 2; i <= n; i++) if(a[i].fi == a[i - 1].fi) pos1 = i;
	for(int i = 2; i <= m; i++) if(b[i].fi == b[i - 1].fi) pos2 = i;
	if(pos1 > 0 && pos2 > 0) {
		cout << a[pos1 - 1].se << " " << a[pos1].se << " " << b[pos2 - 1].se << " " << b[pos2].se << "\n";
		return 0;
	} 
	int cnt = 0; 
	for(int i = 1; i <= n; i++) if(i == 1 || a[i].fi != a[i - 1].fi) a[++cnt] = a[i];
	n = cnt; cnt = 0;
	for(int i = 1; i <= m; i++) if(i == 1 || b[i].fi != b[i - 1].fi) b[++cnt] = b[i];
	m = cnt;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			int v = a[i].fi + b[j].fi;
			if(c[v] == 0) {
				c[v] = 1;
				pos[v] = {a[i].se, b[j].se};
			} else {
				cout << pos[v].fi << " " << a[i].se << " " << pos[v].se << " " << b[j].se << "\n";
				return 0;
			}
		}
	}
	cout << "-1";
	return 0;
}

标签:10,lcp,int,蔚来,pos,枚举,MAXN,题解,define
From: https://www.cnblogs.com/Orzjh/p/16625849.html

相关文章

  • 斯坦福CS107 编程范式07
    探索,使用栈的定义,定义一个通用类型的栈来存储一系列的字符串,并把它们以相反的顺序打印出来。 typedefstruct{void*elems;intelemSize;intloglength......
  • 2022“杭电杯”中国大学生算法设计超级联赛(10) 题解
    C.WavyTree发现修改次数和相邻两数的相对大小有关,所以可先求出差分数组。分两种情况考虑:①奇数位置为波峰②偶数位置为波峰。以情况①为例,若奇数位置差分后值小......
  • win10系统中修改hosts文件
    进入hosts文件目录选择以管理员身份打开WindowsPowerShell输入cmd进入管理员界面,输入notepadhosts编辑hosts文件......
  • Idea创建Maven Web工程的web.xml版本问题解决
    问题使用Maven创建web工程的时候,创建出来的web.xml版本有问题。临时解决方案将在Tomcat安装目录下的webapps/ROOT/WEB-INF下的web.xml替换项目下的web.xml......
  • 10.Java中Map的entrySet() 详解以及用法
    一、Map.entry是什么?Map是java中的接口,Map.Entry是Map的一个内部接口。此接口为泛型,定义为Entry<K,V>。它表示Map中的一个实体(一个key-value对)接口中有get......
  • jmeter-特殊问题解决
    1、相应报文乱码问题:方法一:1、在相应节点的下方,比如http请求,添加后置处理器–》BeanShellPostProcessor2、然后在其脚本框中添加如下代码prev.setDataEncoding(“UTF-8......
  • Educational Codeforces Round 106 (Rated for Div. 2) | CF1499
    E一个暴力是显然的,\(f(i,j,k)\)表示当前已经使用\(a\)的前\(i\)位,\(b\)的前\(j\)位,最后一位是\(a\)还是\(b\)的。然后\(O(n^2)\)枚举起点跑下去即可。为啥......
  • [Oracle] LeetCode 1010 Pairs of Songs With Total Durations Divisible by 60
    Youaregivenalistofsongswheretheithsonghasadurationoftime[i]seconds.Returnthenumberofpairsofsongsforwhichtheirtotaldurationinsecon......
  • 【问题解决】npm ERR! code EINTEGRITY
    问题说明Jenkins构建前端安装依赖报错:npmERR!codeEINTEGRITY11:05:42npmERR!sha512-IJy2B5Ot9wIAGwjSKF94+8yhVCQUDBT4myzlswuJSNPcLcn3Jna3yPNOmp/mbXfPPSNFwV9......
  • 洛谷P1001 A+B problem最全算法
    为防止大家说我误导新人,先放一个最不正常的代码。#include<iostream>usingnamespacestd;intmain(){inta,b;cin>>a>>b;cout<<a+b;ret......