牛客小白月赛97题解

A. 三角形

将所有边的长度存到桶里,最后判断桶里是否有大于等于 3 的数。

B. 好数组

判断数组中是否有0,有0就不是好数组,否则就是好数组。

C. 前缀平方和序列

考虑构造出序列的前缀和序列,前缀和序列确定,这个序列也确定了。

所以问题就成为了有多少个长度为 n 的递增序列,每个元素都在 1 到 x 之间。

即从 1 到 x 中选出 n 个平方数的方案。

求解组合数可以用杨辉三角。

D. 走一个大整数迷宫

注意到 始终等于 ,所以 的值与 没有关系。

然后考虑使用 表示起点到 计数器模 的值为 的最短时间。

cpp代码:

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const int N=12;
const int M=10011;
int a[N][N];
int b[N][N];
int mod;
int dist[N][N][M];
bool st[N][N][M];
int main(){
	ios;
	int  n,m,p;
	cin>>n>>m>>p;
	if(p==2){
		cout<<n+m-2<<endl;
		return 0;
	}
	mod=p-1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
			a[i][j]%=mod;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>b[i][j];
		}
	}
	queue<array<int,3>>q;
	q.push({1,1,a[1][1]});
	int dir[4][2]={1,0,0,1,-1,0,0,-1};
	st[1][1][a[1][1]]=1;
	while(q.size()){
		auto [x,y,z]=q.front();
		q.pop();
		for(int i=0;i<4;i++){
			int xx=x+dir[i][0];
			int yy=y+dir[i][1];
			if(xx<=n&&xx>0&&yy<=m&&yy>0&&st[xx][yy][(z+a[xx][yy])%mod]==0){
				int zz=(z+a[xx][yy])%mod;
				dist[xx][yy][zz]=dist[x][y][z]+1;
				st[xx][yy][zz]=1;
				q.push({xx,yy,zz});
			}
		}
	}
	
	if(st[n][m][0]==0)cout<<-1<<endl;
	else cout<<dist[n][m][0]<<endl;
    return 0;
}

E. 前缀和前缀最大值

对于一个序列 ,考虑如何快速计算它的 类价值。

考虑 类价值最小的方案,是从小到大排序序列

然后进行 次,将序列 的最后一个数字移动到最前面。

会发现这样操作序列 类价值是单调不减的,并且每次只移动一个数,增量只可能是 或者

所以可以得出一个结论, 类价值是连续的。

所以只要求出序列 类价值的上界 和下界 ,就可以得出 类价值为

类价值的上界就是序列 从大到小排序。

然后考虑如何处理多次询问。

上界很好算,就是区间正数个数。

然后考虑下界。

由于发现序列 的值域很小,所以询问的一个区间的正数的种数最多只有 种。

使用前缀和可以快速的求出这些数。

然后就可以使用上述方法求出上界。

cpp代码:

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
int sum[100010][101];
int nesum[100010];
int main(){
	ios;
	int n;
	cin>>n;
	vector<int>a(n+1);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]<0)nesum[i]=nesum[i-1]-a[i];
		else nesum[i]=nesum[i-1];
		for(int j=1;j<=100;j++){
			sum[i][j]=sum[i-1][j]+(a[i]==j);
		}
	}
	int q;
	cin>>q;
	for(int c=1;c<=q;c++){
		int l,r;
		cin>>l>>r;
		int tmp=0;
		int t=nesum[r]-nesum[l-1];
		int res=0;
		int cnt=0;
		for(int i=1;i<=100;i++){
			int j=sum[r][i]-sum[l-1][i];
			if(j!=0){
				if(i*j+res>=t){
					cnt+=(t-res)/i;
					break;
				}
				else cnt+=j,res+=i*j;
			}
		}
		cout<<cnt+1<<endl;
	}
    return 0;
}

F .parent 树上启发式合并

询问的不同的字符串长度和不超过10000。

也就意味着不同的字符串长度只有150种左右。

然后将询问离线。

字符串哈希之后从左到右遍历所有这些长度的子串,同时进行记录这些子串的出现次数,假如这个出现次数的子串在被询问过,就记录答案,比如遍历到一个子串T,并且是第 次出现,恰好有一个询问是 ,那么就可以记录答案了。

这个过程使用unordered_map或者手写哈希表均可通过。

cpp代码:

#include<bits/stdc++.h>
using namespace std;
#define PII pair<int,int>
#define fi first
#define se second
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl '\n'
#define vii vector<vector<int>>
#define ull unsigned long long
const int N = 10000;
vector<pair<ull, int>> S[N];
vector<pair<ull, int>> g[N];
vector<pair<ull, int>> cnt0[N];
int &find(vector<pair<ull, int>> f[], ull u) {
	int hu = u % N;
	for (auto &[i, j]: f[hu]) {
		if (i == u)return j;
	}
	f[hu].emplace_back(u, 0);
	return f[hu].back().se;
}
int count(vector<pair<ull, int>> f[], ull u) {
	int hu = u % N;
	for (auto &[i, j]: f[hu]) {
		if (i == u)return 1;
	}
	return 0;
}
int ans[100010];
ull hq[100010];
int que[100010];
ull p[100010];
ull h[100010];
const int B=131;
void init(const string& s){
	for (int i = 1; i <= s.size()-1; i++) h[i] = h[i - 1] * B + s[i];
}
ull get(int l, int r) { return h[r] - h[l - 1] * p[r - l + 1]; }

int main(){
	ios;
	int n, q;
	cin >> n >> q;
	p[0]=1;
	for(int i=1;i<=100000;i++){
		p[i] = p[i - 1]*B;
	}
	string s;
	cin >> s;
	s = " " + s;
	vector<int> b;
	string t;
	for (int i = 1; i <= q; i++) {
		cin >> t >> que[i];
		t = ' ' + t;
		init(t);
		hq[i] = get(1, t.size() - 1);
		find(g, hq[i]  * p[que[i]]) = i;
		b.push_back(t.size() - 1);
		find(S, hq[i] ) = 1;
	}
	for (int i = 1; i <= q; i++) {
		ans[i] = -1;
	}
	std::sort(b.begin(), b.end());
	b.erase(std::unique(b.begin(), b.end()), b.end());
	init(s);
	for (auto len: b) {
		for (int i = len; i <= n; i++) {
			ull ha = get(i - len + 1, i);
			if (count(S, ha)) {
				find(cnt0, ha) += 1;
				if (count(g, ha * p[find(cnt0, ha)])) {
					ans[find(g, ha * p[find(cnt0, ha)])] = i;
				}
			}
		}
	}
	for (int i = 1; i <= q; i++)cout << ans[find(g, hq[i] * p[que[i]])] << endl;
	return 0;
}