codeforces Round #541

abstract:

I构造题可能代码简单证明很难

II拓扑排序

III并查集 启发式排序,带链表

IV dp 处理字符串递推问题

V 数据结构巧用:于二叉树同构

VI更新了头文件,将很难敲的东西放到define里面,初始化数组可以a[100]={};

C

题意

给你100个人,让你将它们围成一个圆,使得:“任意相邻的两人身高差的绝对值” 中的最大值 最小


题解

显然的构造方法:先排序,让所有人1 2 报数,报2的出列,排尾变排头接到报 1 的原队列后面

证明:

显然这样的构造方法保证身高差最大为 max{(a[i+2]-a[i]) }(i=1..n,环状,a[i]升序);

我们可以说明对于任意的i,身高差至少为(a[i+2]-a[i]),

如果我们将每个人看成一个点,相邻关系看成一条无向边,那么可以作图如下:

不难发现题目等价于构造一条哈密尔顿回路(i.e.从一点出发,走过所有点,且每个点只经过一次,再回到起点)。

反证法:如果存在一条更优的路线,那么我们就不能使用a[i+2]-a[i]这条边以及比这条长的所有边。

同时我们发现去掉这些边以后,a[i+1]变成了割点——从左边到右边必须经过,回到左边时还要经过,违反了初始约定(哈密尔顿回路)。

矛盾。

得证。


代码



D

题意

有两行数,不知道他们的实际值,但知道两行数含有的数字个数,以及行与行所有数字两两之间的大小关系,求一组解使得最大的数最小,若无解特判掉。

input
3 3
>>>
<<<
>>> output
Yes
3 1 3
2 2 2

题解

拓扑排序板题加并查集

建图,从小的数向大的数建有向边,然后拓扑排序,输出他们的优先级即可。

唯一的trick是关系中有=,我们只要把=关系的结点缩点即可,具体实现用并查集,输出时再还原。

拓扑排序用的是khan算法(还有一种倒着dfs的算法)i.e. 队列压入0点,bfs一下,访问到某个点时,将其入度减1,减到0时,压到队列尾部。判断是否有环的方法是记录入栈的点数(每个点只会被压入一次),如果小于总点数,则说明有环。

codeforces的bug:用了加速cin后,puts("")输出的东西会跑到在cout总输出的后面??!!!


代码

//邻接表建图
#include<algorithm>
#include<iostream>
#include<sstream>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<math.h>
#include<stdio.h>
#include<vector>
#include<queue>
#include<string>
#include<ctime>
#include<stack>
#include<map>
#include<set>
#include<list>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define REP(i,j,k) for(int i = (int)j;i < (int)k;i ++)
#define per(i,j,k) for(int i = (int)j;i >= (int)k;i --)
#define debug(x) cerr<<#x<<" = "<<(x)<<endl
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define MD(x) x%=mod
//#define x first
//#define y second
typedef double db;
typedef long long ll;
const int MAXN = 30;;
const int maxn = 2e3+2;
const int INF = 1e9;
const db eps = 1e-7;
const int mod = 1e9 + 7; vector<int> a, b;
//a[maxn], b[maxn];
int A[maxn];
int n,m;
int fa[maxn];
int vis[maxn], dep[maxn],ind[maxn],id[maxn],ans[maxn],ians[maxn];
void init() { for (int i = 1; i <= n+m; i++) fa[i] = i,id[i]=i; }
int find(int x) { if (fa[x] == x) { return x; }return fa[x] = find(fa[x]); }
void un(int x, int y) { int xx = find(x), yy = find(y); fa[yy] = x; } char mmp[maxn][maxn];
vector<int> E[maxn];
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr); cin >> n>>m; //n += m;
init();
rep(i, 1,n )cin >> mmp[i]+1;
rep(i, 1, n)rep(j, 1,m) if (mmp[i][j] == '=') {
un(i, j + n);
} rep(i, 1, n)rep(j, 1, m) {
int x = find(i), y = find(j + n);
if (mmp[i][j] == '<') {
E[x].push_back(y);
ind[y]++;
}
if (mmp[i][j] == '>') {
E[y].push_back(x);
ind[x]++;
}
} int cnt = 0;
rep(i, 1, m + n)if (fa[i] == i)E[0].push_back(i), ind[i]++, cnt++; queue<int> Q;
Q.push(0);
int tmp = 0;
vis[0] = 1;
while (!Q.empty()) {
int u = Q.front();
Q.pop();// vis[u] = 1;
for (int i = 0; i < E[u].size(); i++) {
int v = E[u][i];
if (fa[v] == v) {
ind[v]--;
if (ind[v] == 0) {
Q.push(v); tmp++;
dep[v] = dep[u] + 1;
}
}
} }
//rep(i, 1, n + m)debug(dep[i]);
if (tmp < cnt) {
puts("No"); return 0;
}
cout << "Yes" << endl;
rep(i, 1, n + m) { ians[i] = dep[i]; }
rep(i, 1, n + m) { int u = find(i); ans[i] = ians[u]; }
rep(i, 1, n)cout << ans[i] << ' '; cout << endl;
rep(j, 1, m)cout << ans[n+j] << ' ';
cout << endl;
//cin >> n;
}
/* */

E

题意

定义字符串的乘法: s1*s2就是将s2插入s1的所有相邻字符之间,再在首尾接上s2

sample:
bnn*a=abanana
a*b*a=abaaaba

现在给你1e5个字符串,问最终串的结果中所有字母都相同的最长子串(后面称作重复子串)的长度。


题解

考虑不求出最终串如何维护最长串。 发现每次乘上一个串,我们只要关注这个串的最长重复前后缀即可,然后就可以从n个串推到n+1个串的乘积。

于是考虑dp,f[i]代表前i个串的乘积中最长的重复子串,对每个串,线性处理出最长前后缀pre,suf以及串内最长重复子串len,f[i]=max(len,pre+suf+1),

我们发现这个公式的前提是所有字符都相同,所以我们给f加一维,f[i][j],其中j表示字符('a'..'z')

于是我们得出递推公式(令当前串s的s.length()为L):

if(pre==L)f[i][j]=max(len,L*f[i-1][j]+L+f[i][j])

else if(f[i-1][j]) f[i][j]=max(len,pre+1+suf);

最后更新答案要注意是对f[n]['a'..'z']更新,因为之前的答案可能比之后的大。


代码

#include<algorithm>
#include<iostream>
#include<sstream>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<math.h>
#include<stdio.h>
#include<vector>
#include<queue>
#include<string>
#include<ctime>
#include<stack>
#include<map>
#include<set>
#include<list>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define REP(i,j,k) for(int i = (int)j;i < (int)k;i ++)
#define per(i,j,k) for(int i = (int)j;i >= (int)k;i --)
#define debug(x) cerr<<#x<<" = "<<(x)<<endl
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define MD(x) x%=mod
#define FAST_IO ios_base::sync_with_stdio(false); cin.tie(nullptr)
#define precise(x) fixed << setprecision(x) //#define x first
//#define y second
typedef double db;
typedef long long ll;
const int MAXN = 256;;
const int maxn = 2e5+2;
const int INF = 1e9;
const db eps = 1e-7;
const int mod = 1e9 + 7; //a[maxn], b[maxn];
int A[maxn];
int n,m;
int fa[maxn];
void init() { for (int i = 1; i <= n + m; i++) fa[i] = i; }
int find(int x) { if (fa[x] == 0) { return x; }return fa[x] = find(fa[x]); }
//int Find(int u) { return fa[u] == 0 ? u : fa[u] = Find(fa[u]); }
void un(int x, int y) { int xx = find(x), yy = find(y); fa[yy] = x; } vector<int> E[maxn];
pair<int, int> son[maxn];
string s;
int f[maxn][256];//f[i][j] 代表前i个串的乘积中 只有字符j的最长子串长度。
int main() {
FAST_IO;
cin >> n;
int ans = 0;
rep(i, 1, n) {
cin >> s;
int l = s.length(), t[256] = {}, r[256] = {};
rep(j, 0, l - 1) {
if ((j&&s[j - 1] == s[j]))t[s[j]]++; else t[s[j]] = 1;
r[s[j]] = max(r[s[j]], t[s[j]]);
}
rep(j, 'a','z') {
f[i][j] = r[j];
int t1 = 0, t2 = l - 1; while (s[t1] == j && t1 < l)t1++;
while (s[t2] == j && t2 > t1)t2--;
if (t1 == l)f[i][j] = max(f[i][j], f[i - 1][j] * l + l + f[i - 1][j]);//递推计算乘积中最长序列
else if (f[i - 1][j])f[i][j] = max(f[i][j], t1 + l - t2);
if (i == n)ans = max(ans, f[i][j]);
}
}
cout << ans << endl;
cin >> n;
}
/*
. */

F

题意

有一行数,不知道每个数具体的数值,现在把每个数看成一个集合,给你一系列合并操作x y,表示合并xy所在的集合,已知每次只能合并相邻的集和,求初始的这行数的可能解,保证有解。


题解

算法如下:

将集合看成二叉树,我们每次合并两个集合时建立一个新的结点,作为新集合的根。 这里用并查集O(1)地查询到根,并做合并操作。

执行完所有合并之后得到了一颗二叉树,对其进行先序遍历,到叶子时输出数字即可。

证明:

叶子对应于原数组的数。

每个新的结点对应于一个合并操作,

二叉树上连着根的两棵子树是相邻的,对应于每次合并相邻的集合。

所以叶子从左到右对应着一个解,

用先序即可输出。


代码

#include<algorithm>
#include<iostream>
#include<sstream>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<math.h>
#include<stdio.h>
#include<vector>
#include<queue>
#include<string>
#include<ctime>
#include<stack>
#include<map>
#include<set>
#include<list>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define REP(i,j,k) for(int i = (int)j;i < (int)k;i ++)
#define per(i,j,k) for(int i = (int)j;i >= (int)k;i --)
#define debug(x) cerr<<#x<<" = "<<(x)<<endl
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define MD(x) x%=mod
#define FAST_IO ios_base::sync_with_stdio(false); cin.tie(nullptr)
#define precise(x) fixed << setprecision(x) //#define x first
//#define y second
typedef double db;
typedef long long ll;
const int MAXN = 30;;
const int maxn = 3e5+2;
const int INF = 1e9;
const db eps = 1e-7;
const int mod = 1e9 + 7; //a[maxn], b[maxn];
int A[maxn];
int n,m;
int fa[maxn];
void init() { for (int i = 1; i <= n + m; i++) fa[i] = i; }
int find(int x) { if (fa[x] == 0) { return x; }return fa[x] = find(fa[x]); }
//int Find(int u) { return fa[u] == 0 ? u : fa[u] = Find(fa[u]); }
void un(int x, int y) { int xx = find(x), yy = find(y); fa[yy] = x; } vector<int> E[maxn];
pair<int, int> son[maxn];
void dfs(int u) {
if (u <= n) {
cout << u << ' ';
return;
}
dfs(son[u].first); dfs(son[u].second);
}
int main() {
FAST_IO; cin >> n;
int u, v;
//init();
rep(i, 1, n-1) {
cin >> u >> v;
u = find(u); v = find(v);
fa[u] = fa[v] = n + i;
son[n + i] = { u,v };
}
dfs(find(1));
cin >> n;
}
/*
Simple explanation: when we merge 2 sets, we create a new node stands for the whole set and set its two sons to that 2 sets.
After all operations it will become a tree because answer always exists.
Use DFS to travel this tree, and every time when we meet a leaf node just print it.
It's correct because for every merge operation the 2 operands are sons of a new node and that guarantees the 2 part must be neighbors in the final output. */
//another simplest solution is based on a set-size version of rank heuristic
//uses standard disjoint-set data structure storing lists of elements in each set
struct DSU {
vector<int> par;
vector<int> sz;
vector<list<int>> parts;
DSU(int n) {
FOR(i, 0, n) {
par.pb(i);
sz.pb(1);
parts.pb({i});
}
} int find(int a) {
return par[a] = par[a] == a ? a : find(par[a]);
} bool same(int a, int b) {
return find(a) == find(b);
} void unite(int a, int b) {
a = find(a);
b = find(b);
if(a == b) return;
if(sz[a] > sz[b]) swap(a, b);
// sz[a] <= sz[b]
sz[b] += sz[a];
par[a] = b;
parts[b].splice(parts[b].end(), parts[a]);
}
}; int main() {
FAST_IO;
startTime(); int n;
cin >> n;
DSU dsu(n);
FOR(i, 0, n-1) {
int u, v;
cin >> u >> v;
u--, v--;
u = dsu.find(u);
v = dsu.find(v);
dsu.unite(u, v);
}
auto l = dsu.parts[dsu.find(0)];
for(auto x : l) {
cout << x+1 << " ";
}
cout << endl; timeit("Finished");
return 0;
}

Codeforces Round div2 #541 题解的更多相关文章

  1. Codeforces Round #543 Div1题解(并不全)

    Codeforces Round #543 Div1题解 Codeforces A. Diana and Liana 给定一个长度为\(m\)的序列,你可以从中删去不超过\(m-n*k\)个元素,剩下 ...

  2. Codeforces Round #545 Div1 题解

    Codeforces Round #545 Div1 题解 来写题解啦QwQ 本来想上红的,结果没做出D.... A. Skyscrapers CF1137A 题意 给定一个\(n*m\)的网格,每个 ...

  3. Codeforces Round #539 Div1 题解

    Codeforces Round #539 Div1 题解 听说这场很适合上分QwQ 然而太晚了QaQ A. Sasha and a Bit of Relax 翻译 有一个长度为\(n\)的数组,问有 ...

  4. Educational Codeforces Round 64 部分题解

    Educational Codeforces Round 64 部分题解 不更了不更了 CF1156D 0-1-Tree 有一棵树,边权都是0或1.定义点对\(x,y(x\neq y)\)合法当且仅当 ...

  5. Educational Codeforces Round 64部分题解

    Educational Codeforces Round 64部分题解 A 题目大意:给定三角形(高等于低的等腰),正方形,圆,在满足其高,边长,半径最大(保证在上一个图形的内部)的前提下. 判断交点 ...

  6. Educational Codeforces Round 63部分题解

    Educational Codeforces Round 63 A 题目大意就不写了. 挺简单的,若果字符本来就单调不降,那么就不需要修改 否则找到第一次下降的位置和前面的换就好了. #include ...

  7. Codeforces #Round 376 部分题解

    A: 题目传送门:http://codeforces.com/problemset/problem/731/A 直接根据题意模拟即可 #include "bits/stdc++.h" ...

  8. [Codeforces Round #431]简要题解

    来自FallDream的博客,未经允许, 请勿转载,谢谢. 好久没写cf题解了zzz 代码比较丑不贴了,cf上都可以看 Div2A. 给你一个长度为n(n<=100)的序列 判断是否可以分成奇数 ...

  9. codeforces 576 div2 A-D题解

    A题 Description 题目链接: https://codeforces.com/contest/1199/problem/A 题意: 给定长度为n(1≤n≤100000)的一个序列a,以及两个 ...

随机推荐

  1. ECMAScript各版本简介及特性

    术语 ECMAScript Sun(现在的Oracle)公司持有着“Java”和“JavaScript”的商标.这就让微软不得不把自己的JavaScript方言称之为“JScript”.然后,在这门语 ...

  2. adb shell am broadcast 手动发送广播及adb shell am/pm其他命令

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zi_zhe/article/details/72229201 在命令行可用adb shell am ...

  3. Mybatis抛出:Cannot obtain primary key information from the database, generated objects may be incomplete

    使用 mybatis generator 生成pojo.dao.mapper时,可能会遇到 Cannot obtain primary key information from the databas ...

  4. Atitit 热烈庆祝读经器项目圆满完成

    Atitit 热烈庆祝读经器项目圆满完成 1.1. 读经器项目简单介绍 1 1.2. 一万小时定律和十年一个专家定律 1 1.3. 获得加持前景 1 1.4. 核心源码 1 1.5. 项目git 2 ...

  5. js正则匹配html标签中的style样式和img标签

    <!DOCTYPE html> <html> <head> <title></title> </head> <body&g ...

  6. EntityFramework安装失败

    PM> Install-Package EntityFramework正在尝试收集与目标为“.NETFramework,Version=v4.0”的项目“ConsoleApplication1” ...

  7. 如何配置JVM系统属性及获取方式System.getProperty("pname")

    https://www.cnblogs.com/keyi/p/7721893.html

  8. js this 引起的祸

    注意:这里的 this 指向已经改变了. 所以不能再次使用 this 需在上面自定义变量. 那些年踩过的坑,记录一下.

  9. [转] tomcat 7/8 启动非常慢的解决方法

    在日志中发现启动慢的地方: -- ::] INFO o.s.c.s.DefaultLifecycleProcessor - Starting beans -- ::] INFO o.s.web.con ...

  10. MA5680T OLT管理软件,全智能判断板卡,无人值守策略,根据光猫类型自动扫描添加光猫

    轻量型的ONU查询工具,智能查询板卡,查询自动发现,搜索ONU等功能,需要更多智能功能,可联系QQ:561454825 下载地址:下载地址1