Loading

【总结】COCI2016-2017#4

2021/7/15

T1 :Bridž

水,略

T2 :Kartomat

建个 Trie 树跑一波,不过这么小的数据范围貌似不用

T3:Kas

不难发现就是将 \(N\) 个数分成 \(3\) 堆,前两堆大小相同,使得第三堆最小。

定义状态 \(f[i][j][k]\) 表示前面 \(i\) 个数,第 \(1\) 堆大小为 \(j\),第 \(2\) 堆大小为 \(k\) ,是否可行,直接转移,貌似可以 bitset 优化做到 \(\mathcal{O}(\dfrac{N(\sum c)^2}{w})\)

比较套路的做法,对后两个维度作差,定义状态 \(f[i][j]\) 表示前 \(i\) 个数,前两堆差为 \(j\) 时,第 \(1\) 堆可以达到的最大重量时多少。

直接转移 \(\mathcal{O}(N\sum c)\) 已经可以通过。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
using namespace std;
int f[N << 1], n, g[N << 1];
inline void ck(int &x,int y){if(y > x)x = y;}
int main(){
	scanf("%d", &n);
	int sum = 0;
	memset(f, 0xcf, sizeof(f));
	int bas = N - 5;
	f[bas] = 0;
	rep(i, 1, n){
		memset(g, 0xcf, sizeof(g));
		int x;scanf("%d", &x);
		rep(j, -sum, sum)
			ck(g[j + bas], f[j + bas]), 
			ck(g[j + x + bas], f[j + bas] + x), 
			ck(g[j - x + bas], f[j + bas]);
		sum += x;rep(j, -sum, sum)f[j + bas] = g[j + bas];
	}
	printf("%d\n", sum - f[bas]);
	return 0;
}

T4:Rekonstruiraj

感觉是个错题啊,像 \(\frac{1}{3}\) 这样无限小数怎么处理精度啊。

考虑逆向思维,我们要将最终集合的数一一删去。

显然最小的数只能被不大于它的数删去,那么我们枚举整个数。

而这个数一定是最小的数除以一个正整数倍率,我们从小到大枚举倍率,同时判断在这个倍率下,区间\([A,B]\) 中是否有删除了没有的元素。

由于 \(K\) 很小,所以判断和删除都是 \(\mathcal{O}(K)\) 级别的,所以复杂度是 \(\mathcal{O}(K^2)\) ?感觉挺玄学的。

另外赛时发现 luogu 的 spj 全是锅,直接 Hack spj 就能过。

T5:Rima

挺有意思的题。

刚开始看非常没有思路就先开 T6 了。

先想到可能是 DAG 求最长路,发现不大可做。

观察一下这个 \(LCS \ge Len - 1\) 的条件。两个串长度不相等,一定一个是另一个的前缀且长度相差 \(1\) ,否则一定两个串最后以为一定不相等。

所以长度相等的串且前缀相等大概是两两连边,然后不等的串连边是唯一的。感觉也不是很可做。

但是把这个连边放到 Trie 树上乱搞,发现这就是一个走 \(Trie\) 树的过程。我们要找的是是一条路径,使得路径长度加上与路径直接相连的边的长度之和最大。

直接树上 DP 即可,时间复杂度 \(\mathcal{O}(N + \sum|S|)\),需要注意的是 Trie 的空间,大概得用 vector 维护以时间换空间。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 3000005
using namespace std;
int n, idx, ed[N];
vector<pair<int,int>>ch[N];
char s[N];
int get(int x,int y){
	for(auto z:ch[x])if(z.first == y)return z.second;
	return 0;
}
int f[N], ans;
void dfs(int x){
	f[x] = ed[x];
	int mx = 0, nxt = 0, sz = 0;
	for(auto y:ch[x]){
		dfs(y.second);
		if(ed[y.second]){
			sz++, nxt = max(nxt, f[y.second]);
			if(nxt > mx)swap(nxt, mx);
		}
	}ans = max(ans, nxt + mx + f[x] + max(0, sz - 2));
	f[x] += mx + max(0, sz - 1);
}
int main(){
	scanf("%d", &n);
	rep(i, 1, n){
		scanf("%s", s + 1);
		int m = strlen(s + 1), cur = 0;
		pre(j, m, 1){
			int now = get(cur, s[j] - 'a');
			if(!now)
				ch[cur].push_back(make_pair(s[j] - 'a', ++idx)), cur = idx;
			else cur = now;
		}
		ed[cur] = 1;
		//cout<<"ww "<<cur<<endl;
	}
	dfs(0);printf("%d\n", ans);
	return 0;
}

T6:Osmosmjerka

求概率输出准确值还不用取模,显然是直接枚举所有可能(

显然这个无限矩阵就是以给定的子矩阵为元不断循环,如果我们固定一个方向,那么以 \((i,j)\)为起点的串,和以 \((i+an, j+bm),\ a,b\in \mathcal{Z}\) 为起点的串一定相同。

所以我们只用枚举起点在 \((i, j), i\in[0, n - 1], j\in[0,m -1]\) 的串即可,然后就是求这个串的哈希值。

这个字符串开始和结束的几个单独拎出来,中间不断循环。开始和结束的哈希值可以预处理,中间的是个等比数列,可以直接通项公式爆算。

细节比较多。注意斜的,横的,竖的的循环节可能都不相同,斜的循环节和 \(n,m\) 的最大公约数有关,不过我们可以直接对整个子矩阵遍历一遍避免过多讨论。

另外建议双哈希,赛时随机了一个\(10^9\) 左右的质数 \(934567889\) 被卡了 \(40\) 分,然后又随了两个质数还是挂了,查了半天还以为是哪里挂了,后来才发现原来是卡了哈希,估计出题人把 \(10^9\) 左右的模数都卡了。

后来找到 \(bas = 229, P = 10^9 + 97\) 没有卡(

时间复杂度 \(\mathcal{O}(NM\log)\) ,实现较丑还排了序。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 505
#define P 1000000097
#define bas 229
using namespace std;
int n, m, k, v[N][N], mat[N][N], idx, f[N][N], pw[N * N];
vector<int>c[N];
char s[N][N], cp[N][N];
void ins(int x,int y){
	v[x][y] = ++idx;mat[x][y] = 0;
	c[idx].push_back(s[x][y]);
	while(true){
		x = (x + 1) % n;
		y = (y + 1) % m;
		if(v[x][y])break ;
		v[x][y] = idx, 
		mat[x][y] = c[idx].size(), 
		c[idx].push_back(s[x][y]);
	}
	int w = c[idx].size();
	//cout<<"init " <<w<<endl;
	rep(i, 1, w - 1)c[idx][i] = (1LL * c[idx][i - 1] * bas + c[idx][i]) % P;
}
void init(){
	rep(i, 0, n - 1){
		f[i][0] = s[i][0];
		rep(j, 1, m - 1)
			f[i][j] = (1LL * f[i][j - 1] * bas + s[i][j]) % P;
	}
	rep(i, 0, n - 1)rep(j, 0, m - 1)if(!v[i][j])ins(i, j);
}
vector<int>w;
int Pow(int x,int y){
	int now = 1;
	for(;y;y >>= 1, x = 1LL * x * x % P)if(y & 1)now = 1LL * now * x % P;
	return now;
}
inline int g(int x,int y){
	if(x == 1)return y + 1;
	return 1LL * (Pow(x, y + 1) - 1) * Pow(x - 1, P - 2) % P;
}
int calc(int x,int bs,int cnt){return 1LL * x * g(bs, cnt - 1) % P;}
int row(int x,int y){
	int res = k, pr = 0, cur = 0;
	if(y)pr = f[x][y - 1];
	if(m - y >= res)return (f[x][y + res - 1] - 1LL * pr * pw[res] % P + P) % P;
	cur = (f[x][m - 1] - 1LL * pr * pw[m - y] % P + P) % P, res -= m - y;
	if(res <= m)return (1LL * pw[res] * cur + f[x][res - 1]) % P;
	int cc = res / m; res %= m;
	cur = 1LL * cur * Pow(bas, cc * m) % P;
	cur = (cur + calc(f[x][m - 1], pw[m], cc)) % P;
	if(!res)return cur;
	return (1LL * pw[res] * cur + f[x][res - 1]) % P;
}
int col(int x,int y){
	int t = v[x][y]; 
	y = mat[x][y], x = t;
	int w = c[x].size(), res = k, pr = 0, cur = 0;
	if(y)pr = c[x][y - 1];
	if(w - y >= res)return (c[x][y + res - 1] - 1LL * pr * pw[res] % P + P) % P;
	cur = (c[x][w - 1] - 1LL * pr * pw[w - y] % P + P) % P, res -= w - y;
	if(res <= w)return (1LL * pw[res] * cur + c[x][res - 1]) % P;
	int cc = res / w; res %= w;
	cur = 1LL * cur * Pow(bas, cc * w) % P;
	cur = (cur + calc(c[x][w - 1], pw[w], cc)) % P;
	if(!res)return cur;
	return (1LL * pw[res] * cur + c[x][res - 1]) % P;
}
void rotate(){
	rep(i, 0, n - 1)rep(j, 0, m - 1)cp[j][n - 1 - i] = s[i][j];
	swap(n, m);
	rep(i, 0, n - 1)rep(j, 0, m - 1)s[i][j] = cp[i][j];
}
long long gcd(long long x,long long y){return y ? gcd(y, x % y) : x;}
signed main(){
	scanf("%d%d%d", &n, &m, &k);
	pw[0] = 1;rep(i, 1, n * m)pw[i] = 1LL * pw[i - 1] * bas % P;
	rep(i, 0, n - 1)scanf("%s", s[i]);
	rep(op, 0, 3){
		memset(v, 0, sizeof(v));
		rep(i, 1, idx)c[i].clear();
		idx = 0;init();
		rep(i, 0, n - 1)rep(j, 0, m - 1)
			w.push_back(row(i, j)), 
			w.push_back(col(i, j));
		rotate();
	}
	sort(w.begin(), w.end());int sum = 0, pr = ~0;long long ans = 0;
	for(int x : w){
		if(pr != x)ans += 1LL * sum * sum, sum = 0, pr = x;
		sum++;
	}ans += 1LL * sum * sum;//cout<<endl;
	long long fac = 1LL * w.size() * w.size();
	long long cur = gcd(ans, fac);
	printf("%lld/%lld\n", ans / cur, fac / cur);
	return 0;
}
posted @ 2021-07-15 11:53  7KByte  阅读(211)  评论(0编辑  收藏  举报