杂题乱做

大概就找几个题瞎写了一下。

1.[GXOI/GZOI2019]旅行者

洛谷

题意

给你一张有向图,有 \(k\) 个关键点,让你求这 \(k\) 个点之间的最短距离是多少。

数据范围: \(n\leq 10^5,m\leq 5\times 10^5\)

题解

把这 \(k\) 个点二进制分组,然后跑最短路即可。

由于是有向图,所以要跑两遍。

复杂度:\(O(n\log^2n)\) 。据说还有带一只 \(\log\) 的做法,但我不太会。

2.[TJOI2019]甲苯先生和大中锋的字符串

洛谷

题意

给你一个字符串,让你求在这个字串中出现了 \(k\) 次的子串,在这些子串中子串长度出现次数最多的长度数。

数据范围: \(1\leq n\leq 10^5,\sum n\leq 3\times 10^6\)

题解

\(TJ\) 省选为什么会出后缀自动机的裸题啊。

先对这个字符串建立后缀自动机,然后合并出 \(\text{endpos}\) 的大小,对于 \(\text{endpos}\) 大小为 \(k\) 的节点,会对其 \(minlen(i)\sim maxlen(i)\) 的出现次数有贡献。差分一下即可。

3.CF360C Levko and Strings

洛谷

题意

给一个为 \(n\) 的只有小写字母组成的串 \(s\),定义它与一个长度为 \(n\) 的串 \(t\) 的的美丽度为:\(c\) 存在多少个二元组 \((i,j) \ 1\leq i \leq j \leq n\) 满足 \(s[i...j] < t[i...j]\),这里的 \('<'\) 是字典序比较。求有多少个 \(t\) ,使得 \(s\)\(t\) 的美丽度为 \(k\)

数据范围: \(n,k \leq 2000\)

题解

\(f[i][k]\) 表示 \(t\) 串填到第 \(i\) 个字符,美丽度为 \(k\) 的方案数。

转移的话考虑枚举上一个 \(s_j < t_j\) 的地方,中间的 \(t_{j+1}\sim t_{i-1}\)\(s_{j+1}\sim s_{i-1}\) 字符串完全相同。

\(s_{k} > t_k\) 则有:\(f[i][k] = \displaystyle\sum_{j=0}^{i-1} f[j][k] \times (s[i]- \text{'a'})\)

\(s_k < t_k\) 则有:\(f[i][k] = \displaystyle\sum_{j=0}^{i-1} f[j][k-(n-i+1)(i-j)] \times (\text{'z'}-s[i])\)

第一个转移的话前缀和优化一下就好了。

第二个转移显然是不会超过 \(\sqrt{n}\) 次的。

所以复杂度为 \(O(nk\sqrt{n})\)

4.[八省联考2018]劈配

洛谷

题目太长了,懒得简化了。

题解

对于第一问的话,很容易想到一个二分图的模型,有源点向每个选手连一条容量为 \(1\) 的边,由导师向汇点连一条容量为 \(b_i\) 的边,如果第 \(i\) 名选手填了第 \(j\) 名导师,则有 \(i\)\(j+n\) 连一条容量为 \(1\) 的边。每次枚举每个选手的每一档志愿,同时向这一档志愿中的导师连边,如果当前网络的最大流增加了 \(1\), 则说明这个选手会被当前枚举到的这一档志愿录取。

第二问的话,不难想到二分答案,设其为 \(\text{mid}\) ,然后把前 \(i-mid-1\) 的选手对应的最优录取方案加进去。同时把第 \(i\) 位选手的前 \(s_i\) 档志愿所对应的边连上。求一遍最大流, 如果最大流等于 \(i-mid\) (这里要排除被淘汰的学生)则说明这个答案是合法的。

一个小优化:每次可以把一些没有用的边删掉,这样会快很多。

加上这个优化每次暴力重构都可以过去,就不用了写麻烦的删边操作了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
const int inf = 1e7+10;
const int N = 410;
int T,n,m,s,t,x,c,tot = 1;
int head[N],b[N],a[N],p[N],dep[N],Q[N][N][N],cnt[N][N],cur[N];
struct node
{
	int to,net,w;
}e[300010];
queue<int> q;
inline int read()
{
    int s = 0,w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    return s * w;
}
void add(int x,int y,int w)
{
	e[++tot].to = y;
	e[tot].w = w;
	e[tot].net = head[x];
	head[x] = tot;
}
bool bfs()
{
	for(int i = 0; i <= t; i++) cur[i] = head[i], dep[i] = 0;
	while(!q.empty()) q.pop();
	q.push(s); dep[s] = 1;
	while(!q.empty())
	{
		int x = q.front(); q.pop();
		for(int i = head[x]; i; i = e[i].net)
		{
			int to = e[i].to;
			if(e[i].w && !dep[to])
			{
				dep[to] = dep[x] + 1;
				q.push(to);
				if(to == t) return 1;
			}
		}
	}
	return 0;
}
int dinic(int x,int flow)
{
	if(x == t || !flow) return flow;
	int rest = flow, val;
	for(int i = cur[x]; i && rest; i = e[i].net)
	{
		cur[x] = i;
		int to = e[i].to;
		if(!e[i].w || dep[to] != dep[x]+1) continue;
		val = dinic(to,min(e[i].w,rest));
		if(val == 0) dep[to] = 0;
		e[i].w -= val, e[i^1].w += val, rest -= val;
	}
	return flow - rest;
}
int Maxflow()
{
	int res = 0, flow = 0;
	while(bfs())
	{
		while(flow = dinic(s,inf)) res += flow;
	}
	return res;
}
bool judge(int x,int mid)
{
	tot = 1; int num = 0;
	memset(head,0,sizeof(head));
	for(int i = 1; i <= m; i++) add(n+i,t,b[i]), add(t,n+i,0);
	for(int i = 1; i <= x-mid-1; i++)
	{
		add(s,i,1); add(i,s,0); 
		if(p[i] != m+1)
		{
			num++;
			for(int j = 1; j <= cnt[i][p[i]]; j++) add(i,n+Q[i][p[i]][j],1), add(n+Q[i][p[i]][j],i,0);
		}
	}
	add(s,x,1); add(x,s,0);
	for(int i = 1; i <= a[x]; i++)
	{
		for(int j = 1; j <= cnt[x][i]; j++) add(x,n+Q[x][i][j],1), add(n+Q[x][i][j],x,0);
	}
	if(Maxflow() == num+1) return 1;
	return 0;
}
void Qk()
{
	tot = 1;
	memset(head,0,sizeof(head));
	memset(p,0,sizeof(p));
	memset(cnt,0,sizeof(cnt));
}
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	T = read(); c = read();
	while(T--)
	{
		n = read(); m = read();
		s = 0, t = n+m+1;
		for(int i = 1; i <= m; i++) b[i] = read();
		for(int i = 1; i <= m; i++) add(n+i,t,b[i]), add(t,n+i,0);
		for(int i = 1; i <= n; i++)
		{
			for(int j = 1; j <= m; j++)
			{
				x = read();
				Q[i][x][++cnt[i][x]] = j;
			}
		}
		for(int i = 1; i <= n; i++)
		{
			add(s,i,1); add(i,s,0);
			for(int j = 1; j <= m; j++)
			{
				for(int k = 1; k <= cnt[i][j]; k++) add(i,n+Q[i][j][k],1), add(n+Q[i][j][k],i,0);
				if(Maxflow() != 0){p[i] = j; break;}
				else for(int k = 1, u = tot; k <= cnt[i][j]; k++, u -= 2) e[u].w = e[u^1].w = 0;//每次把没用的边都删掉。 
			}
			if(!p[i]) p[i] = m+1;
			printf("%d ",p[i]);
		}
		printf("\n");
		for(int i = 1; i <= n; i++) a[i] = read();
		for(int i = 1; i <= n; i++)
		{
			if(p[i] <= a[i])printf("%d ",0);
			else
			{
				int L = 1, R = i-1, ans = i;
				while(L <= R)
				{
					int mid = (L + R)>>1;
					if(judge(i,mid))
					{
						ans = mid;
						R = mid - 1;
					}
					else L = mid + 1;
				}
				printf("%d ",ans);
			}
		}
		printf("\n"); Qk();
	}
	return 0;
}

5.AGC038C LCMs

洛谷

题意

给你一个序列 \(a_i\), 让你求 \(\displaystyle\sum_{i=1}^{n}\sum_{j=i+1}^{n} \text{lcm}(a_i,a_j)\) 。答案对 \(998244353\) 取模。

数据范围:\(1\leq n\leq 2\times 10^5,1\leq A_i\leq 10^6\)

题解

莫比乌斯反演。

我们考虑先把 \(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{n}\text{lcm}(a_i,a_j)\) 求出来,然后减去 \(\sum a_i\) ,在除以 \(2\), 就是题目中要我们求的东西的答案。

考虑怎么求 \(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{n}\text{lcm}(a_i,a_j)\) 。莫反一波。

\(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{n}\text{lcm}(a_i,a_j)\)

\(=\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{n} {a_ia_j\over \gcd (a_ia_j)}\)

\(=\displaystyle\sum_{d=1}^{n} {1\over d}\sum_{i=1}^{n}\sum_{j=1}^{n} (a_ia_j)\times [\gcd (a_i,a_j = d]\)

\(=\displaystyle\sum_{d=1}^{n}{1\over d}\sum_{p=1}^{n\over d}\mu(p)\sum_{i=1}^{n}\sum_{j=1}^{n} (a_i,a_j)\times [p\mid \gcd({a_i\over d},{a_j\over d})]\)

\(Q = dp\) 则有:

\(原式=\displaystyle\sum_{Q=1}^{n}\sum_{i=1}^{n} [Q\mid a_i]a_i\sum_{j=1}^{n}[Q\mid a_j]a_j\sum_{d\mid Q}{1\over d}\mu({n\over d})\)

\(\displaystyle\sum_{i=1}^{n}[{Q\mid a_i}]a_i\) ,每个 \(a_i\) 枚举他的约数然后算一下就好了。

后面的 \(\displaystyle f(i) = \sum_{d\mid i}{1\over d}\mu({n\over d})\) 。这个也是可以线性筛筛出来的。

复杂度:\(O(n\sqrt{n}+n)\)

ps:以后要少用 #define int long long,这玩意常数贼大,用了这个 \(20s+\), 不用这个 \(7s+\) ,就 \(nm\) 离谱.

6.P4051 [JSOI2007]字符加密

洛谷

题意

题面懒得简化了 \(\text{QAQ}\)

题解

先把整个字符串复制一倍,在接到原串的后面。

然后求一下后缀排序就好了。

7.P4070 [SDOI2016]生成魔咒

洛谷

题意

\(n\) 次操作,每次操作在 \(s\) 的结尾加入一个字符。每次操作后问你当前字符串本质不同的子串个数。

数据范围:\(n\leq 10^5,x_i\leq 10^9\)

题解

本质不同的子串个数可以用后缀自动机来求,即 \(\displaystyle \sum_{u} maxlen(u)-maxlen(link(u))\)

然后这道题就可以用 \(\text{LCT}\) 动态来维护了

一个简单的做法,对整个串建立一个后缀自动机,然后每次添加新字符的时候维护一下答案的增量就好了。

由于这道题的字符集很大,所以要用 \(\text{map}\) 来存状态。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
using namespace std;
#define LL long long
const int N = 5e5+10;
int n,x,cnt,last,link[N],len[N];
LL ans;
inline int read()
{
    int s = 0,w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    return s * w;
}
map<int,int> tr[N];
void Extend(int ch)
{
	int cur = ++cnt, p;
	len[cur] = len[last] + 1;
	for(p = last; p && !tr[p].count(ch); p = link[p]) tr[p][ch] = cur;
	if(!p) link[cur] = 1, ans += 1LL * len[cur]-len[link[cur]];
	else
	{
		int x = tr[p][ch];
		if(len[x] == len[p] + 1) link[cur] = x, ans += 1LL * len[cur]-len[link[cur]];
		else
		{
			int y = ++cnt;
			len[y] = len[p] + 1;
			tr[y] = tr[x];
			ans -= 1LL * len[x] - len[link[x]];
			link[y] = link[x];
			link[x] = link[cur] = y;
			ans += 1LL * len[y]-len[link[y]];
			ans += 1LL * len[x]-len[link[x]];
			ans += 1LL * len[cur]-len[link[cur]];
			while(p && tr[p][ch] == x)
			{
				tr[p][ch] = y;
				p = link[p];
			}
		}
	}
	last = cur;
}
int main()
{
	n = read();
	last = cnt = 1;
	for(int i = 1; i <= n; i++)
	{
		x = read();
		Extend(x);
		printf("%lld\n",ans);
	}
	return 0;
}

8.CF802I Fake News (hard)

洛谷

题意

给你一个字符串,让你求 \(\sum_{p}cnt(s,p)^2\)

\(cnt(s,p)\) 表示子串 \(p\)\(s\) 中的出现次数。

数据范围:\(|s| \leq 10^5, T\leq 10\)

题解

后缀自动机裸题。

先对整个串建立后缀自动机,然后求出每个节点 \(\text{endpos}\) 的大小。

然后答案为 \(\sum_{u} (max_len(u)-minlen(u))\times |endpos(u)|^2\)

9.CF852B Neural Network country

洛谷

题意

有一幅有向图,除了源点和汇点有 \(L\) 层,每层 \(n\) 个点。 第 \(i+1\) 层的每个点到第 \(i+2\) 层的每个点都有一条边,边的权值为有向边终点的权值。求源点到汇点的路径长度能被 \(m\) 整除的个数。

数据范围: \(2\leq n\leq 10^6,L\leq 10^5,m\leq 100\)

题解

\(f[i][j]\) 表示当前在第 \(i\) 层,从源点到这一层的点的路径长度 \(\%m\)\(j\) 的路径个数。

转移的话则有:\(f[i][j] =\sum_{k} f[i-1][(j-b_k+m)\% m]\)

这个直接矩阵快速幂优化一下就好了。

但这个做法有个缺陷,就是到最后两层我们还需要记录到当前路径以那个点结尾,如果把这个也设进状态的话,那复杂度就是 \(O(n^3k^3)\) 的了。

解决办法也很简单,只需要把后面那两层拿出来单独讨论一下就好了。

时间复杂度:\(O(nm+m^3logk)\)

10.P5231 [JSOI2012]玄武密码

洛谷

题意描述

给你一个只包含 \(\text{E.S,W,N}\) 的字符串 \(s\),和 \(m\) 个字符串 \(t\) 。让你对每一个 \(t\) 求其最长的前缀 \(p\) ,满足 \(p\)\(s\) 的子串。

数据范围:\(1\leq n\leq 10^7,m\leq 10^5,|t|\leq 100\)

题解

好像有 \(\text{AC自动机}\)\(SAM\) 的两种做法。

做法1 SAM

\(s\) 的子串等价于 \(s\) 的一段后缀的前缀。

我们对 \(s\) 建立后缀自动机,然后对每个 \(t\) 串在上面跑匹配就好了。

由于 \(s\) 只包含 \(\text{E,S,W,N}\) 这四个字符,所以我们 \(trans\) 数组第二维只需要开到 \(4\) 就可以了,这样就可以省下不少空间。

做法2:AC自动机

由于自己写的是上面的那个做法,所以这个口胡一下就好了(不保证正确性)。

我们对 \(t\) 串建立 \(AC\) 自动机,然后对 \(s\) 串在上面进行匹配,并把经过的点标记一下。、

最后在对 \(t\) 串跑一下 \(\text{tire}\) 树(\(AC\) 自动机的插入所构成的 \(\text{tire}\) 树),如果当前节点有标记,就拿它来更新一下答案就好了。

posted @ 2021-04-09 08:07  genshy  阅读(87)  评论(0编辑  收藏  举报