zoukankan      html  css  js  c++  java
  • 杂题乱做

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

    1.[GXOI/GZOI2019]旅行者

    洛谷

    题意

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

    数据范围: (nleq 10^5,mleq 5 imes 10^5)

    题解

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

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

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

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

    洛谷

    题意

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

    数据范围: (1leq nleq 10^5,sum nleq 3 imes 10^6)

    题解

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

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

    3.CF360C Levko and Strings

    洛谷

    题意

    给一个为 (n) 的只有小写字母组成的串 (s),定义它与一个长度为 (n) 的串 (t) 的的美丽度为:(c) 存在多少个二元组 ((i,j) 1leq 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] = displaystylesum_{j=0}^{i-1} f[j][k] imes (s[i]- ext{'a'}))

    (s_k < t_k) 则有:(f[i][k] = displaystylesum_{j=0}^{i-1} f[j][k-(n-i+1)(i-j)] imes ( ext{'z'}-s[i]))

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

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

    所以复杂度为 (O(nksqrt{n}))

    4.[八省联考2018]劈配

    洛谷

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

    题解

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

    第二问的话,不难想到二分答案,设其为 ( ext{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("
    ");
    		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("
    "); Qk();
    	}
    	return 0;
    }
    

    5.AGC038C LCMs

    洛谷

    题意

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

    数据范围:(1leq nleq 2 imes 10^5,1leq A_ileq 10^6)

    题解

    莫比乌斯反演。

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

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

    (displaystylesum_{i=1}^{n}sum_{j=1}^{n} ext{lcm}(a_i,a_j))

    (=displaystylesum_{i=1}^{n}sum_{j=1}^{n} {a_ia_jover gcd (a_ia_j)})

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

    (=displaystylesum_{d=1}^{n}{1over d}sum_{p=1}^{nover d}mu(p)sum_{i=1}^{n}sum_{j=1}^{n} (a_i,a_j) imes [pmid gcd({a_iover d},{a_jover d})])

    (Q = dp) 则有:

    (原式=displaystylesum_{Q=1}^{n}sum_{i=1}^{n} [Qmid a_i]a_isum_{j=1}^{n}[Qmid a_j]a_jsum_{dmid Q}{1over d}mu({nover d}))

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

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

    复杂度:(O(nsqrt{n}+n))

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

    6.P4051 [JSOI2007]字符加密

    洛谷

    题意

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

    题解

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

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

    7.P4070 [SDOI2016]生成魔咒

    洛谷

    题意

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

    数据范围:(nleq 10^5,x_ileq 10^9)

    题解

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

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

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

    由于这道题的字符集很大,所以要用 ( ext{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
    ",ans);
    	}
    	return 0;
    }
    

    8.CF802I Fake News (hard)

    洛谷

    题意

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

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

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

    题解

    后缀自动机裸题。

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

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

    9.CF852B Neural Network country

    洛谷

    题意

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

    数据范围: (2leq nleq 10^6,Lleq 10^5,mleq 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]玄武密码

    洛谷

    题意描述

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

    数据范围:(1leq nleq 10^7,mleq 10^5,|t|leq 100)

    题解

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

    做法1 SAM

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

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

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

    做法2:AC自动机

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

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

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

  • 相关阅读:
    centos7安装es6.4.0
    将mysql数据同步到ES6.4(全量+增量)
    c#基于supersocket的简单websocket服务端收发消息实现
    c#log4net简单好用的配置
    MongoDB安装配置教程
    IntelliJ IDEA 中创建maven项目
    VMware Workstation 的安装和使用
    Redis使用场景
    Redis 下载安装
    MySQL--启动和关闭MySQL服务
  • 原文地址:https://www.cnblogs.com/genshy/p/14635635.html
Copyright © 2011-2022 走看看