题目链接

还缺F和G,至少上橙之后把F补了吧。

A - Palindromic Twist

题意:每个字母恰好操作一次,变成其之前或者其之后的一个字母,注意'a'和'z'不互通,求是否可以变成回文串。

题解:居然是不互通?!

char a[2005];

void test_case() {
int n;
scanf("%d%s", &n, a + 1);
for(int i = 1; i <= n / 2; ++i) {
if(a[i] > a[n - i + 1])
swap(a[i], a[n - i + 1]);
if(a[i] == a[n - i + 1])
continue;
if(a[n - i + 1] - a[i] != 2) {
puts("NO");
return;
}
}
puts("YES");
}

B - Numbers on the Chessboard

题意:给一个n*n的棋盘格,找规律,然后回答题目询问的特定格子的数字。先涂左上角(设为黑色),黑白棋盘格,只涂黑色,从1开始一行一行只涂黑色,涂到右下角。再涂白色,也是从左上角的白色开始。

题解:行列加起来是偶数的就是黑色格。当n是偶数的时候每行都一样的黑白格更好涂。首先观察到(x-1)/2的下整刚好有这么多个n,先加上去,然后判断x是不是零头,是零头就把他这一行的黑/白的数量加上。y直接取除以2的上整。好简单哦,所以当时在写什么呢?这题比焦作那个蜂巢最短路简单好多啊。

ll n;
void solve(ll x, ll y) {
if((x + y) % 2 == 0) {
ll res = 0;
res += (x - 1) / 2 * n;
if(x % 2 == 0)
res += (n + 1) / 2;
res += (y + 1) / 2;
printf("%lld\n", res);
} else {
ll res = (n * n + 1) / 2;
res += (x - 1) / 2 * n;
if(x % 2 == 0)
res += n / 2;
res += (y + 1) / 2;
printf("%lld\n", res);
}
} void test_case() {
int q;
scanf("%lld%d", &n, &q);
while(q--) {
ll x, y;
scanf("%lld%lld", &x, &y);
solve(x, y);
}
}

C - Minimum Value Rectangle

题意:给n根木棒,选4根组成长方形,使得这个长方形的周长的平方比上其面积最小。

题解:对那个式子求导,发现对于同一个长来说,是长和宽越接近,上式越小。那么排序之后每个和他附近的一个组装一下就行了。

map<int, int> m;
vector<int> v; void test_case() {
int n;
scanf("%d", &n);
m.clear();
for(int i = 1; i <= n; ++i) {
int ai;
scanf("%d", &ai);
m[ai]++;
}
v.clear();
for(auto &j : m) {
if(j.second == 2 || j.second == 3)
v.push_back(j.first);
else if(j.second >= 4) {
printf("%d %d %d %d\n", j.first, j.first, j.first, j.first);
return;
}
}
ll fz, fm, a, b;
n = v.size();
for(int i = 0; i < n - 1; ++i) {
ll nfz = 2ll * (v[i] + v[i + 1]);
nfz *= nfz;
ll nfm = 1ll * v[i] * v[i + 1];
if(i == 0 || nfz * fm < fz * nfm) {
a = v[i];
b = v[i + 1];
fz = nfz;
fm = nfm;
}
}
printf("%lld %lld %lld %lld\n", a, a, b, b);
}

事实上可能不需要map,直接来一波快排,然后跑一遍nxt。

int a[1000005];
int b[1000005]; void test_case() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
int w, h, btop = 0;
for(int i = 1, nxt; i <= n; i = nxt) {
for(nxt = i + 1; nxt <= n && a[nxt] == a[i]; ++nxt);
int cnt = nxt - i;
if(cnt >= 4) {
printf("%d %d %d %d\n", a[i], a[i], a[i], a[i]);
return;
}
if(cnt >= 2)
b[++btop] = a[i];
}
ll fz, fm;
for(int i = 1; i < btop; ++i) {
ll nfz = 2ll * (b[i] + b[i + 1]);
nfz *= nfz;
ll nfm = 1ll * b[i] * b[i + 1];
if(i == 1 || nfz * fm < fz * nfm) {
w = b[i];
h = b[i + 1];
fz = nfz;
fm = nfm;
}
}
printf("%d %d %d %d\n", w, w, h, h);
}

D - Mouse Hunt

题意:有n个房间,有1个老鼠,开始可能在任意一个房间,在第i个房间放陷阱花ci,老鼠在第i个房间下一次就会到ai。求最便宜的陷阱总额。

题解:基环树的内向树。乱搞一点直接套Kosaraju缩点,然后缩点之后的代表点放这堆点的最小值,然后把所有0出度的ci加起来。

不过这个跟反图没有任何关系,加边然后去重甚至都不需要加边和去重直接统计出度(因为0始终是0,非0的出度去重后出度也不会是0)。

namespace SCC {
const int MAXN = 2e5; int n;
vector<int> G[MAXN + 5], BG[MAXN + 5]; int c1[MAXN + 5], cntc1;
int c2[MAXN + 5], cntc2;
int s[MAXN + 5], cnts; int n2;
vector<int> V2[MAXN + 5];
//vector<int> G2[MAXN + 5], BG2[MAXN + 5]; const int INF = 0x3f3f3f3f;
int C1[MAXN + 5], C2[MAXN + 5]; void Init(int _n) {
n = _n;
cntc1 = 0, cntc2 = 0, cnts = 0;
for(int i = 1; i <= n; ++i) {
G[i].clear();
BG[i].clear();
c1[i] = 0;
c2[i] = 0;
s[i] = 0;
V2[i].clear();
//G2[i].clear();
//BG2[i].clear(); C2[i] = INF;
}
for(int i = 1; i <= n; ++i)
scanf("%d", &C1[i]);
for(int i = 1, v; i <= n; ++i) {
scanf("%d", &v);
G[i].push_back(v);
BG[v].push_back(i);
}
} /*void AddEdge(int u, int v) {
G[u].push_back(v);
BG[v].push_back(u);
}*/ void dfs1(int u) {
c1[u] = cntc1;
for(int v : G[u]) {
if(!c1[v])
dfs1(v);
}
s[++cnts] = u;
} void dfs2(int u) {
V2[cntc2].push_back(u);
C2[cntc2] = min(C2[cntc2], C1[u]);
c2[u] = cntc2;
for(int v : BG[u]) {
if(!c2[v])
dfs2(v);
}
} void Kosaraju() {
for(int i = 1; i <= n; ++i) {
if(!c1[i]) {
++cntc1;
dfs1(i);
}
} for(int i = n; i >= 1; --i) {
if(!c2[s[i]]) {
++cntc2;
dfs2(s[i]);
}
}
} /*void Build() {
n2 = cntc2;
for(int i = 1; i <= n2; ++i) {
for(auto u : V2[i]) {
for(auto v : G[u]) {
if(c2[v] != i) {
G2[i].push_back(c2[v]);
BG2[c2[v]].push_back(i);
}
}
}
} for(int i = 1; i <= n2; ++i) {
sort(G2[i].begin(), G2[i].end());
G2[i].erase(unique(G2[i].begin(), G2[i].end()), G2[i].end());
sort(BG2[i].begin(), BG2[i].end());
BG2[i].erase(unique(BG2[i].begin(), BG2[i].end()), BG2[i].end());
}
}*/ void Solve() {
ll ans = 0;
n2 = cntc2;
for(int i = 1; i <= n2; ++i) {
bool cnt = 0;
for(auto u : V2[i]) {
for(auto v : G[u]) {
if(c2[v] != i) {
cnt = 1;
break;
}
}
if(cnt)
break;
}
if(!cnt)
ans += C2[i];
}
printf("%lld\n", ans);
}
} void test_case() {
int n;
scanf("%d", &n);
SCC::Init(n);
SCC::Kosaraju();
SCC::Solve();
}

要是真按基环树去做,是不是要从入度0的点一个一个dfs下去找环,然后有环回溯的时候顺带找一波最小值?老套路用个全局变量记录重复点的id,直到回溯到它都标记为环。注意dfs的时候要染色,遇到其他颜色dfs过的就直接退出。不过我这不是有现成的缩点模板吗?

const int MAXN = 2e5;
const int INF=0x3f3f3f3f; int n, G[MAXN + 5];
int color[MAXN + 5], cntcolor; int val[MAXN + 5]; ll ans;
int minval; int incircle;
void dfs(int u, int c) {
if(color[u]) {
if(color[u] == c) {
incircle = u;
return;
}
//op1
return;
}
color[u] = c;
dfs(G[u], c);
if(incircle) {
//op2
minval = min(minval, val[u]);
if(u == incircle) {
//op3
ans += minval;
incircle = 0;
}
return;
}
//op1
} void test_case() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &val[i]);
for(int i = 1; i <= n; ++i)
scanf("%d", &G[i]);
ans = 0;
cntcolor = 0;
for(int i = 1; i <= n; ++i) {
if(!color[i]) {
++cntcolor;
minval = INF;
dfs(i, cntcolor);
}
}
printf("%lld\n", ans);
}

还有更神奇的做法是用并查集做的!我擦,把整棵基环树合并到环上面,不过这样做就更复杂了判一大堆情况。

最简单的方法就是直接缩点变成DAG!

E - Inverse Coloring

题意:一个n*n的棋盘格,黑白染色,其中每一行必须与相邻行相同或相反。且不能有任何一个同色矩形有超过k个小块。求方案数。

题解:首先对第1行dp,设dp[i][j][0/1]表示第1行的前i个格子,其中最大连续为j个,0表示已经断了,1表示还在连续,那么dp[n][1k][0+1]分别进行k次统计就可以了。然后竖着dp,dp[i][j][0/1]也是前i个格子,最大连续为j个,0表示断了,1表示连续,为什么是这样呢?因为每一行的颜色都是一样的,所以可以简化这个问题。最后对于行最大连续值为1的就可以是dp[n][1][0+1]*dp[n][1k][0+1],2的就dp[n][1][0+1]*dp[n][1~k/2][0+1]。

上面的很有问题,因为有可能会重新连续过多而超过历史的最大连续,修正后为这样:dp[i][j][k][0/1]表示第1行的前i个格子,其中最大连续为j个,当前末尾连续为k个,0和1表示颜色。gp[i][j][k]表示sum(dp[i][j][1~k]),用来方便dp转移。

转移的时候,接在后面的格子假如换色,那么dp[i][j][1][0]=gp[i][j][j][1],因为末尾不超过j连续的历史最大j连续的以1位结尾的加一个0就变成只有1个连续的。否则连续数必定大于等于2,由同色转移而来,注意当j==k时可以额外从dp[i][j-1][k-1]转移而来,突破历史的上限(看n=4的样例时发现)。

最后要求每一行同色或反色,那么把列的第1行就强制被行锁住,颜色数恰好是一半(黑白正好对称),再求一个前缀和qp[j]表示前n列最大连续数不超过j的方法数(或者n^2暴力)求出来。注意在k%i==0的时候要再-1这样乘起来才不超过k,且这个乘积值不能超过ck。

注:取模实在是特别慢,鉴于前面全部都是加法转移,则只需要在溢出前最后取一下模就够了,和超大的模数LMOD比较,发现它准备相加溢出才取模。

const ll MOD = 998244353ll;
const ll LMOD = 998244353ll * 100000000ll; ll dp[2][505][505][2];
ll gp[2][505][505][2];
ll qp[505]; void test_case() {
int n, k;
scanf("%d%d", &n, &k);
int ck = min(n, k);
dp[1][1][1][0] = 1;
dp[1][1][1][1] = 1;
gp[1][1][1][0] = 1;
gp[1][1][1][1] = 1;
for(int i = 2; i <= n; ++i) {
int t = i & 1;
int cj = min(i, ck);
for(int j = 1; j <= cj; ++j) {
dp[t][j][1][0] = gp[1 - t][j][j][1];
dp[t][j][1][1] = gp[1 - t][j][j][0];
for(int l = 2; l <= j; ++l) {
dp[t][j][l][0] = dp[1 - t][j][l - 1][0];
dp[t][j][l][1] = + dp[1 - t][j][l - 1][1];
if(l == j) {
dp[t][j][l][0] += dp[1 - t][j - 1][l - 1][0];
dp[t][j][l][1] += dp[1 - t][j - 1][l - 1][1];
}
}
for(int l = 1; l <= j; ++l) {
if(dp[t][j][l][0] >= LMOD)
dp[t][j][l][0] %= MOD;
if(dp[t][j][l][1] >= LMOD)
dp[t][j][l][1] %= MOD;
gp[t][j][l][0] = gp[t][j][l - 1][0] + dp[t][j][l][0];
if(gp[t][j][l][0] >= LMOD)
gp[t][j][l][0] %= MOD;
gp[t][j][l][1] = gp[t][j][l - 1][1] + dp[t][j][l][1];
if(gp[t][j][l][1] >= LMOD)
gp[t][j][l][1] %= MOD;
}
}
} int t = n & 1;
for(int j = 1; j <= ck; ++j)
qp[j] = (qp[j - 1] + gp[t][j][j][0]) % MOD; ll ans = 0;
for(int i = 1; i <= ck; ++i)
ans += (gp[t][i][i][0] + gp[t][i][i][1]) % MOD * qp[min(ck, k / i - (k % i == 0))] % MOD; ans %= MOD;
printf("%lld\n", ans);
}

这道题还有n^2的解法,太烧脑了。不管怎么说,dp大概逐渐有2100的实力了。

F - Session in BSU

题意:有n门考试,第i门考试正考是在ai,补考是在bi,一天只能最多考1门,求是否有一种方法可以通过所有考试,若有,输出用时最小的。

题解:假如没有这个用时最小的限制,是不是就是2-SAT呢?一共有n个变量,第一种方法是取值为ai,第二种方法是取值为bi,离散化到2e6内之后,连到同一天的每一对i,j,都满足若i取ai则j取bj以及若j取aj则i取bi。求个强连通分量就可以知道是不是-1,那么假如有解怎么构造呢?考虑一种假做法:二分一个最终值x,x的上限为max(min(ai,bi)),下限为min(max(ai,bi))?,在这个范围里面保证每个变量至少能有1天。然后对于只剩下一种选择的全部强制赋值?想不清楚。

还有人用并查集过的?https://blog.csdn.net/ModestCoder_/article/details/97237866和https://www.geek-share.com/detail/2745662240.html(并查集维护二分图匹配)

也有人说这个是基环树?的确,把所有天数离散之后,考试就变成了一条出边,但为什么是基环树?https://www.cnblogs.com/yqgAKIOI/p/9804609.html

图论有点难了。

Educational Codeforces Round 49 (Rated for Div. 2)的更多相关文章

  1. Educational Codeforces Round 49 (Rated for Div. 2)A到C题

    A题意 给你t表示有t组测试数据,每组数据给你一个含小写字母的字符串,每个字符必须变为它相邻的字符,问最后是否能变成回文串.a不能变成z,反过来也不行 分析 只需对对称位置判断差是否小于2且不等于1, ...

  2. 【Educational Codeforces Round 49 (Rated for Div. 2) 】

    A:https://www.cnblogs.com/myx12345/p/9843826.html B:https://www.cnblogs.com/myx12345/p/9843869.html ...

  3. Educational Codeforces Round 60 (Rated for Div. 2) - C. Magic Ship

    Problem   Educational Codeforces Round 60 (Rated for Div. 2) - C. Magic Ship Time Limit: 2000 mSec P ...

  4. Educational Codeforces Round 60 (Rated for Div. 2) - D. Magic Gems(动态规划+矩阵快速幂)

    Problem   Educational Codeforces Round 60 (Rated for Div. 2) - D. Magic Gems Time Limit: 3000 mSec P ...

  5. Educational Codeforces Round 43 (Rated for Div. 2)

    Educational Codeforces Round 43 (Rated for Div. 2) https://codeforces.com/contest/976 A #include< ...

  6. Educational Codeforces Round 35 (Rated for Div. 2)

    Educational Codeforces Round 35 (Rated for Div. 2) https://codeforces.com/contest/911 A 模拟 #include& ...

  7. Codeforces Educational Codeforces Round 44 (Rated for Div. 2) F. Isomorphic Strings

    Codeforces Educational Codeforces Round 44 (Rated for Div. 2) F. Isomorphic Strings 题目连接: http://cod ...

  8. Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes

    Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes 题目连接: http://code ...

  9. Educational Codeforces Round 63 (Rated for Div. 2) 题解

    Educational Codeforces Round 63 (Rated for Div. 2)题解 题目链接 A. Reverse a Substring 给出一个字符串,现在可以对这个字符串进 ...

随机推荐

  1. Python 内置函数进制转换的用法(十进制转二进制、八进制、十六进制)

    使用Python内置函数:bin().oct().int().hex()可实现进制转换. 先看Python官方文档中对这几个内置函数的描述: bin(x)Convert an integer numb ...

  2. K2 BPM_加班党们,说好不哭还是说好不秃?_流程管理

    早上经过财务小陈的办公桌 看到她正边看手机边默默流泪 诶?这不是这两天刷屏的 周杰伦的<说好不哭>吗 小陈你怎么哭啦,这歌让你想到前男友了吗? ...... (摇头) 小陈擦擦眼泪 唱起了 ...

  3. lumen路由配置nginx

    nginx配置文件中添加: set   $root_path   '/data/www/m.domain.com/public';    root   $root_path; location / { ...

  4. Elasticsearch ES索引

    ES是一个基于RESTful web接口并且构建在Apache Lucene之上的开源分布式搜索引擎. 同时ES还是一个分布式文档数据库,其中每个字段均可被索引,而且每个字段的数据均可被搜索,能够横向 ...

  5. java基本数据类型包装

    1. 2. 左边的是对象,自动装箱为对象,右边的是基本的数据类型. 3. 如果m,n换成128就超出范围,结果就不一样. 是因为把在这区间内的值都放在了常量池里面. Integer m = Integ ...

  6. 深入浅出Git(偏向理论)

    目录 一.理论概述 1. 什么是Git 版本控制系统分类 2. GitLab和GitHub是什么 3.Git功能 二.结合具体命令了解其工作 1.环境 2.部署 Git仓库的使用 简单命令解释 Git ...

  7. 【DRF框架】利用序列化组件操作

    使用序列化组件进行操作 不带参数:查+增 带参数:查.改.删 不带参数的操作 # url路由 url(r'^book_list/$',Search_book.as_view()), # views.p ...

  8. java - day011 - 集合, ArrayList HashMap,HashSet, Iterator 接口, for-each 循环格式

    集合 ArrayList 丑数: 能被3,5,7整除多次, ArrayList     list 接口             | - ArrayList             | - Linked ...

  9. C++——多态实现原理分析

    前言 虚函数执行速度要稍慢一些.为了实现多态性,每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现.所以多态性总是要付出一定代价,但通用性是一个更高的目标. 实验环境 Windo ...

  10. C++——Big Three(copy ctor、copy op=、dtor)

    Big Three C++ 中Big Three指的是copy ctor 和 copy op=  和  dtor m_data是个字符串指针.一般而言,处理字符串,都是使用指针,在需要存储字符的时候再 ...