这周继续坚持早睡早起和晨跑,尤其11点左右睡,加油
周一 3.29(杂题)
发现cf的题真的质量挺高,能学到挺多东西的
以后每周自己去vjudge弄比赛,拉四五道比我水平高一些的题目,比如目前就是cf 1800~1900的题目,然后赛后补题
这样训练效果是最好的,都是比自己水平高一点的题目,不做太难的题也不做太简单的题,同时又有比赛的感觉
B. Playlist(链表 + set)
这题其实思路挺简单,就看你怎么实现这个东西
用链表维护原来的数,用set维护要删掉的数
但是我写的很复杂,调试来去心态爆炸,其实我已经完成了百分之80了
后来我看了题解,以及rank1的代码,真的很优美,以后做cf题多看rank1的代码
发现他在set中用了一个upper_bound大大简化了代码,而我只会用find
还有发现他很喜欢打空行分割代码,我也可以多学习
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e5 + 10;
int a[N], pre[N], nex[N], n;
vector<int> ans;
set<int> s;
int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
int main()
{
int T; scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
_for(i, 1, n)
{
scanf("%d", &a[i]);
if(i == 1) pre[1] = n; else pre[i] = i - 1;
if(i == n) nex[n] = 1; else nex[i] = i + 1;
}
s.clear();
_for(i, 1, n)
if(gcd(a[pre[i]], a[i]) == 1)
s.insert(i);
ans.clear();
int now = 1;
while(!s.empty())
{
auto it = s.upper_bound(now);
if(it == s.end()) it = s.begin();
now = *it;
ans.push_back(now);
if(pre[now] == now) break;
nex[pre[now]] = nex[now];
pre[nex[now]] = pre[now];
s.erase(now);
s.erase(nex[now]);
if(gcd(a[pre[now]], a[nex[now]]) == 1) s.insert(nex[now]);
now = nex[now];
}
printf("%d ", ans.size());
for(int i: ans) printf("%d ", i);
puts("");
}
return 0;
}
周三 3.31 (杂题 + Tarjan)
昨天状态比较差,很困,就没怎么学
体能还是非常重要的,坚持早睡早起和晨跑,维持良好的体能
C. Skyline Photo(dp + 单调栈)
这是一道2100的dp,挺难的,也学到了很多
自己想只想到了很显然O(n^2)的dp
dp[i]表示1到i的最优值,显然枚举i的照片有多长就可以求出
但是没想到怎么优化
看了题解
突破口在于观察到一个性质,观察到这个性质后面就迎刃而解了
当前求dp[i]
那么设最近的小于hi的点在j
那么照片从j到i的时候,这张照片的值就是bj
照片再长的时候,就可以转化成dpj的情况
照片更长时的最优值已经保存在dpj了
因此求dpi可以分为两部分
第一部分是dp[j]
第二部分是照片比较短,还没有到j的情况
第二部分答案就是b[i] + max(dp[j……i-1])
考虑怎么实现
首先找j用到了单调栈,用一个单调栈维护就好
其次dp[j……i-1]的最大值可以用线段树维护
因此复杂度是O(nlogn)的
但是还可以再优化,不用到线段树
再维护单调栈的过程中统计弹出元素的dp值
但是之前已经删掉了一些元素,也就统计不到它们的dp值
那怎么办,我们可以用k数组,把之前删掉元素的dp值保存在里面
也就是k[i]为dp[i]以及加入i时删掉的元素的dp值得最大值
这样就可以O(n)做出这道题目
这里还有一个坑就是可能找不到j,也就是左边得元素全部比i大
那么这时答案就是b[i] + max(所有删除的k)
但是注意可以全部为一张照片,也就是max(max(所有删除的k), 0)
这个地方我WA了一发
这道题还是很精彩的
核心是观察出那个性质,我写的时候没观察出来
O(n)做法
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int N = 3e5 + 10;
int h[N], v[N], n;
ll dp[N], k[N];
stack<int> s;
int main()
{
scanf("%d", &n);
_for(i, 1, n) scanf("%d", &h[i]);
_for(i, 1, n) scanf("%d", &v[i]);
dp[1] = v[1];
s.push(1);
k[1] = dp[1];
_for(i, 2, n)
{
ll mx = -1e18;
while(!s.empty() && h[s.top()] > h[i])
{
mx = max(mx, k[s.top()]);
s.pop();
}
ll t = mx;
if(s.empty()) dp[i] = v[i] + max(mx, 0ll);
else
{
mx = max(mx, dp[s.top()]);
dp[i] = max(v[i] + mx, dp[s.top()]);
}
s.push(i);
k[i] = max(dp[i], t);
}
printf("%lld\n", dp[n]);
return 0;
}
O(nlogn)线段树做法
理解线段树的本质,按照实际需要打线段树
不用结构体写挺好的
#include<bits/stdc++.h>
#define l(k) (k << 1)
#define r(k) (k << 1 | 1)
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int N = 3e5 + 10;
int h[N], v[N], n;
ll dp[N], t[N << 2];
stack<int> s;
void up(int k)
{
t[k] = max(t[l(k)], t[r(k)]);
}
void change(int k, int l, int r, int x, ll p)
{
if(l == r)
{
t[k] = p;
return;
}
int m = (l + r) >> 1;
if(x <= m) change(l(k), l, m, x, p);
else change(r(k), m + 1, r, x, p);
up(k);
}
ll query(int k, int l, int r, int L, int R)
{
if(L <= l && r <= R) return t[k];
ll res = -1e18;
int m = (l + r) >> 1;
if(L <= m) res = max(res, query(l(k), l, m, L, R));
if(R > m) res = max(res, query(r(k), m + 1, r, L, R));
return res;
}
int main()
{
scanf("%d", &n);
_for(i, 1, n) scanf("%d", &h[i]);
_for(i, 1, n) scanf("%d", &v[i]);
dp[1] = v[1];
s.push(1);
change(1, 1, n, 1, dp[1]);
_for(i, 2, n)
{
while(!s.empty() && h[s.top()] > h[i]) s.pop();
if(s.empty()) dp[i] = v[i] + max(query(1, 1, n, 1, i - 1), 0ll);
else dp[i] = max(v[i] + query(1, 1, n, s.top(), i - 1), dp[s.top()]);
change(1, 1, n, i, dp[i]);
s.push(i);
}
printf("%lld\n", dp[n]);
return 0;
}
P3388 无向图割点
理解算法本质。也许几个月后你都不记得具体怎么实现的,但你理解了本质,就可以依靠理解写出代码
Tarjan的核心是dfs序和low数组
一个点祖先的dfs序一定比它小,先搜到
根据这个判断所有后代中有没有边连到祖先
那么就要求low数组,所有后代往上连最上能到多少
根节点要额外处理一下,无向图没有横叉边,所以根节点有2个以上子树时,没了根节点子树就不连通了
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 2e4 + 10;
vector<int> g[N];
int dfn[N], low[N], ans[N], n, m, root, cnt;
void dfs(int u)
{
dfn[u] = low[u] = ++cnt; //low初始化
int son = 0;
for(int v: g[u])
{
if(!dfn[v])
{
dfs(v); //先搜,回溯时再统计。也避免了叶子节点为割点
low[u] = min(low[u], low[v]);
if(u == root && ++son > 1) ans[u] = 1; //注意不是原图上连的边,是搜索树上有多少子树
if(u != root && low[v] >= dfn[u]) ans[u] = 1; //有一个儿子符合就是割点
}
else low[u] = min(low[u], dfn[v]); //注意这里是 dfn[v] 不是low[v]
}
}
int main()
{
scanf("%d%d", &n, &m);
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
_for(i, 1, n)
if(!dfn[i]) //防止图不连通
dfs(root = i);
int sum = 0;
_for(i, 1, n)
if(ans[i])
sum++;
printf("%d\n", sum);
_for(i, 1, n)
if(ans[i])
printf("%d ", i);
return 0;
}
「一本通 3.5 例 1」受欢迎的牛(缩点模板题)
这题具有传递性,因此可以缩点
变成一个有向无环图
按照题意每个点都可以达到所求的点,此时这个点不能向外连边,否则出现环,而这个图是有向无环图
但是如果有两个以上出度为0的点,那么这两个点之间无法喜欢,不符合题意
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e4 + 10;
vector<int> g[N];
int st[N], dfn[N], low[N], num[N], co[N], out[N];
int id, cnt, top, n, m;
void dfs(int u)
{
dfn[u] = low[u] = ++cnt;
st[++top] = u; //新搜到的点入栈
for(int v: g[u])
{
if(!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!co[v]) low[u] = min(low[u], dfn[v]); //更新的前提是还没有被删掉
}
if(low[u] == dfn[u])
{
id++;
while(1)
{
co[st[top]] = id;
num[id]++;
top--;
if(st[top + 1] == u) break; //在栈中从栈顶到u属于一个联通分量
}
}
}
int main()
{
scanf("%d%d", &n, &m);
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
}
_for(i, 1, n)
if(!dfn[i]) //图可能不是联通的
dfs(i);
_for(u, 1, n)
for(int v: g[u])
if(co[u] != co[v]) //属于不同强连通的边
out[co[u]]++;
int sum = 0, ans;
_for(i, 1, id) //枚举每一个强联通
if(!out[i])
{
if(++sum > 1)
{
puts("0");
return 0;
}
ans = num[i];
}
printf("%d\n", ans);
return 0;
}
周四 4.1 (强连通分量)
「一本通 3.5 例 2」最大半连通子图(缩点 + 拓扑排序 + dp)
这类问题是缩点dp,第一次做
缩点的作用就是把有环的图变成有向无环图
没有环之后就有一些新的性质,比如存在拓扑序,可以dp
这道题按照题意缩点之后就变成了最长链,因为不存在环。
然后在DAG上dp,有点类似之前做的一道最短路计数的题目
注意缩点之后就多了很多重边,这些重边会影响答案的统计,要先去重
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e5 + 10;
vector<int> g[N], G[N];
int a[N], b[N], dfn[N], low[N], st[N], co[N];
int dis[N], num[N], in[N], dp[N];
int n, m, x, cnt, top, id, ans;
vector<pair<int, int> > Edge;
void dfs(int u) //缩点
{
dfn[u] = low[u] = ++cnt;
st[++top] = u;
for(int v: g[u])
{
if(!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!co[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u])
{
id++;
while(1)
{
co[st[top--]] = id;
num[id]++;
if(st[top + 1] == u) break;
}
}
}
void build() //建立新图, 点1到id G存边
{
_for(i, 1, n)
if(!dfn[i])
dfs(i);
_for(u, 1, n)
for(int v: g[u])
if(co[u] != co[v])
Edge.push_back(make_pair(co[u], co[v]));
sort(Edge.begin(), Edge.end());
int t = unique(Edge.begin(), Edge.end()) - Edge.begin(); //缩点后有很多重边,去重
REP(i, 0, t)
{
int u = Edge[i].first, v = Edge[i].second;
G[u].push_back(v);
in[v]++;
}
}
void topo() //边topo边dp
{
queue<int> q;
_for(i, 1, id)
if(!in[i])
{
q.push(i);
dp[i] = 1;
dis[i] = num[i];
ans = max(ans, dis[i]); //初始化也要更新答案,小心坑。多写一点周全一点
}
while(!q.empty())
{
int u = q.front(); q.pop();
for(int v: G[u])
{
if(--in[v] == 0) q.push(v);
if(dis[v] < dis[u] + num[v])
{
dis[v] = dis[u] + num[v];
dp[v] = dp[u]; //注意不是为1
ans = max(ans, dis[v]);
}
else if(dis[v] == dis[u] + num[v]) //不去重边的话这个地方会多算
dp[v] = (dp[v] + dp[u]) % x;
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &x);
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
}
build();
topo();
int sum = 0;
_for(i, 1, id)
if(dis[i] == ans)
sum = (sum + dp[i]) % x;
printf("%d\n%d\n", ans, sum);
return 0;
}
周五 4.2 (强连通分量)
P3387 【模板】缩点
这题比昨天那题简单
注意几个点
一.如果要去重的话,unique返回的是end的值
二.如果有重边,则路径条数会多算,是错误的。这道题只是求点权和最大的路径,没有求条数,所以可以不用去重
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e4 + 10;
int a[N], val[N], dfn[N], low[N], co[N], st[N], in[N], dis[N];
int top, id, cnt, n, m;
vector<int> g[N], G[N];
vector<pair<int, int> > Edge;
void dfs(int u)
{
dfn[u] = low[u] = ++cnt;
st[++top] = u;
for(int v: g[u])
{
if(!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!co[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u])
{
id++;
while(1)
{
co[st[top]] = id;
val[id] += a[st[top]];
top--;
if(st[top + 1] == u) break;
}
}
}
void build()
{
_for(i, 1, n)
if(!dfn[i])
dfs(i);
_for(u, 1, n)
for(int v: g[u])
if(co[u] != co[v])
{
G[co[u]].push_back(co[v]);
in[co[v]]++;
}
}
void topo()
{
queue<int> q;
_for(i, 1, id)
if(!in[i])
{
q.push(i);
dis[i] = val[i];
}
while(!q.empty())
{
int u = q.front(); q.pop();
for(int v: G[u])
{
if(--in[v] == 0) q.push(v);
dis[v] = max(dis[v], dis[u] + val[v]);
}
}
}
int main()
{
scanf("%d%d", &n, &m);
_for(i, 1, n) scanf("%d", &a[i]);
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
}
build();
topo();
int ans = 0;
_for(i, 1, id)
ans = max(ans, dis[i]);
printf("%d\n", ans);
return 0;
}
周六 4.3(强连通分量)
最近欠了挺多作业,所以训练量会少一些
「一本通 3.5 练习 2」消息的传递(缩点 + 拓扑排序)
首先可以互相传递,一个强连通分量内可以相互传递,所以可以等价为一个点
然后就变成了有向无环图,可以拓扑排序
如果要遍历所有点,就要从所有入度为0的点开始遍历,有向无环图存在拓扑序,所以最后一定可以遍历完整个图
所以答案就是缩点之后入度为0的点的个数
缩点后不存在自环,重边不影响答案
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e3 + 10;
vector<int> g[N];
int dfn[N], low[N], co[N], st[N], in[N];
int top, cnt, n, id;
void dfs(int u)
{
dfn[u] = low[u] = ++cnt;
st[++top] = u;
for(int v: g[u])
{
if(!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!co[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
id++;
while(1)
{
co[st[top--]] = id;
if(st[top + 1] == u) break;
}
}
}
void build()
{
_for(i, 1, n)
if(!dfn[i])
dfs(i);
_for(u, 1, n)
for(int v: g[u])
if(co[u] != co[v])
in[co[v]]++;
}
int main()
{
scanf("%d", &n);
_for(i, 1, n)
_for(j, 1, n)
{
int x; scanf("%d", &x);
if(x) g[i].push_back(j);
}
build();
int sum = 0;
_for(i, 1, id)
if(!in[i])
sum++;
printf("%d\n", sum);
return 0;
}
「一本通 3.5 练习 4」抢掠计划(缩点 + dp)
和洛谷的模板缩点挺像
这题不一样的地方在于固定了最长链的起点
而洛谷那题没有固定起点
那这个时候就不用拓扑排序了
从起点开始spfa就好
spfa稍微改一下
边权改为点权,求最小改为求最大
spfa的本质其实就是dp
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 5e5 + 10;
vector<int> g[N], G[N];
int dfn[N], low[N], st[N], a[N], k[N], co[N], val[N], vis[N], dp[N];
int n, m, top, s, p, id, cnt;
void dfs(int u)
{
dfn[u] = low[u] = ++cnt;
st[++top] = u;
for(int v: g[u])
{
if(!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!co[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u])
{
id++;
while(1)
{
co[st[top]] = id;
val[id] += a[st[top]];
top--;
if(st[top + 1] == u) break;
}
}
}
void build()
{
_for(i, 1, n)
if(!dfn[i])
dfs(i);
_for(u, 1, n)
for(int v: g[u])
if(co[u] != co[v])
G[co[u]].push_back(co[v]);
}
void work()
{
queue<int> q;
q.push(co[s]);
dp[co[s]] = val[co[s]];
vis[co[s]] = 1;
while(!q.empty())
{
int u = q.front(); q.pop();
vis[u] = 0;
for(int v: G[u])
if(dp[v] < dp[u] + val[v])
{
dp[v] = dp[u] + val[v];
if(!vis[v]) q.push(v);
}
}
}
int main()
{
scanf("%d%d", &n, &m);
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
}
_for(i, 1, n) scanf("%d", &a[i]);
scanf("%d%d", &s, &p);
_for(i, 1, p) scanf("%d", &k[i]);
build();
work();
int ans = 0;
_for(i, 1, p)
ans = max(ans, dp[co[k[i]]]);
printf("%d\n", ans);
return 0;
}
「一本通 3.5 练习 3」间谍网络(缩点)
和前面的题很像
同样考虑入度为0的点就行了
如果不能控制所有间谍的话,那就dfs一下,把所有能经过的点都标记一下就行了
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 3e3 + 10;
vector<int> g[N], G[N];
int dfn[N], low[N], co[N], st[N], in[N], a[N], val[N], vis[N];
int top, cnt, n, id, p, m;
void dfs(int u)
{
dfn[u] = low[u] = ++cnt;
st[++top] = u;
for(int v: g[u])
{
if(!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!co[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
id++;
val[id] = 1e9;
while(1)
{
co[st[top]] = id;
val[id] = min(val[id], a[st[top]]);
top--;
if(st[top + 1] == u) break;
}
}
}
void build()
{
_for(i, 1, n)
if(!dfn[i])
dfs(i);
_for(u, 1, n)
for(int v: g[u])
if(co[u] != co[v])
{
in[co[v]]++;
G[co[u]].push_back(co[v]);
}
}
void search(int u)
{
vis[u] = 1;
for(int v: G[u])
if(!vis[v])
search(v);
}
int main()
{
scanf("%d%d", &n, &p);
_for(i, 1, n) a[i] = 1e9;
while(p--)
{
int i, j;
scanf("%d%d", &i, &j);
a[i] = j;
}
scanf("%d", &m);
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
}
build();
int ans = 0, flag = 0;
_for(i, 1, id)
if(!in[i])
{
if(val[i] == 1e9)
{
flag = 1;
break;
}
ans += val[i];
}
if(!flag)
{
printf("YES\n%d\n", ans);
return 0;
}
_for(i, 1, id)
if(val[i] != 1e9 && !vis[i])
search(i);
puts("NO");
_for(i, 1, n)
if(!vis[co[i]])
{
printf("%d\n", i);
break;
}
return 0;
}
「一本通 3.5 练习 1」网络协议(缩点 + 判断度数猜结论)
我靠自己写写到了92分,一个点过不去。
我的构造方法应该比较接近正解了,但是肯定有什么地方没考虑到
刚了好久,最后看了题解
发现答案很简单,就是max(入度为0的点,出度为0的点)
从度数的角度考虑,强连通是不存在入度和出度为0的点的
所以把出度为0的点向入度为0的点连边,就不存在出入度为0的点
但这里有个问题就是度数对了不代表就是强联通分量
但是这个结论是对的,一定有某种方式可以连成强联通分量
所以就是猜结论,如果要证明得费一番功夫
我自己写的时候其实就是想一种具体的连边方法
其实直接猜结论
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e2 + 10;
vector<int> g[N];
int dfn[N], low[N], co[N], st[N], in[N], out[N];
int top, cnt, n, id;
void dfs(int u)
{
dfn[u] = low[u] = ++cnt;
st[++top] = u;
for(int v: g[u])
{
if(!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!co[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
id++;
while(1)
{
co[st[top--]] = id;
if(st[top + 1] == u) break;
}
}
}
void build()
{
_for(i, 1, n)
if(!dfn[i])
dfs(i);
_for(u, 1, n)
for(int v: g[u])
if(co[u] != co[v])
out[co[u]]++, in[co[v]]++;
}
int main()
{
scanf("%d", &n);
_for(i, 1, n)
{
int x;
while(scanf("%d", &x) && x)
g[i].push_back(x);
}
build();
int sum0 = 0, sum1 = 0;
_for(i, 1, id)
{
if(!in[i]) sum0++;
if(!out[i]) sum1++;
}
if(id == 1) printf("1\n0\n");
else printf("%d\n%d\n", sum0, max(sum0, sum1));
return 0;
}
周日 4.4 (cf补题)
「一本通 3.5 练习 5」和平委员会,想了挺久没有清晰的做法,后来看题解看到是2-sat模板题
这个算法没学过……
以后会专门学这个算法的,先跳过
还好没有卡太久
做题的时候有充分思考还是想不出才看题解,把握好度,不要马上就看,也少死磕
有时候是因为有些知识点没学过
昨晚打了人生中第一场cf
之前因为不想熬夜所以一直没正式打,一般做题或者vp
这次div2 rank 1287
1个小时10分钟前三道题,后来D题卡住了,最后又跑去看E题,事实证明当时还是继续看D题比较好
心态不错,不着急,正常节奏写题思考,这样发挥是最好的
D. 3-Coloring(构造 + 套路)
这和之前一道cf题很像
也是构造一个矩阵,使得相邻的数不相同
那题的做法像国际象棋棋盘,按照奇偶
这题也一样,都是套路,我考试时没想起来这玩意
所以填成
1 2 1
2 1 2
1 2 1
这样就行了,能填1就填1,不能填就填2
然后1和2其中一个填完了,比如1填完了,要填2,
能填1填1,不能填就填3
视线上注意一下细节,比如跳到下一行
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
int n;
void print(int x, int y, int k)
{
printf("%d %d %d\n", k, x, y);
fflush(stdout);
}
void update(int& x, int& y)
{
y += 2;
if(y > n)
{
y = y % 2 + 1;
x++;
}
}
int main()
{
scanf("%d", &n);
int x1 = 1, y1 = 1, x2 = 1, y2 = 2;
int x3, y3, p;
_for(i, 1, n * n)
{
int x; scanf("%d", &x);
if(x == 2 || x == 3)
{
print(x1, y1, 1);
if(x1 == n && y1 == n)
{
p = 2;
break;
}
update(x1, y1);
}
else
{
print(x2, y2, 2);
if(x2 == n && y2 == n - 1)
{
p = 1;
break;
}
update(x2, y2);
}
}
if(p == 2) x3 = x2, y3 = y2;
else x3 = x1, y3 = y1;
while(1)
{
int x; scanf("%d", &x);
if(x != 3) print(x3, y3, 3);
else print(x3, y3, p);
if(x3 == n && (y3 == n - 1 || y3 == n)) break;
update(x3, y3);
}
return 0;
}
听学长讲想外出比赛需要超过一些学长
压力好大啊
突然感觉自己挺懈怠的
下一周搞起
绩点竞赛全都要,做好时间管理