随手的笔记
点性质的思考
对于图中点的状态我们不只是可以通过dfs或者bfs的时候的参数去体现,我们也可以通过建图的时候通过加权的形式去体现(带权并查集的方式)
思路来自-ABC round 456-E - Endless Holidays
这里附上两种体现形式的代码
#include <bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
bool dfs(vector<vector<int>>&g, vector<bool>&vis,vector<bool>&finish ,int x)
{
if(finish[x]) return false;
vis[x] = true;
for(auto v : g[x])
{
if(finish[v]) continue;
if(vis[v] || dfs(g, vis, finish, v)) return true;
}
finish[x] = true;
return false;
}
void solve()
{
int n,m;cin >> n >> m;
vector<string>ci(n);
vector<int>d(n + 1, 0);
vector<pii>ed(m);
for(int i = 0;i < m ;i ++)
{
cin >> ed[i].first >> ed[i].second;
-- ed[i].first;
-- ed[i].second;
}
for(int i = 0; i < n ;i ++) ed.push_back(make_pair(i, i));
int w;cin >> w;
for(int i = 0 ;i < n ;i ++) cin >> ci[i];
vector<vector<int>>g(n * w);
for(int i =0 ; i < ed.size() ; i ++)
{
int x = ed[i].first, y = ed[i].second;
for(int i = 0;i < w ;i ++)
{
int nx = (i + 1) % w;
if(ci[x][i] == 'o' && ci[y][nx] == 'o')g[n * i + x].push_back(n * nx + y);
if(ci[y][i] == 'o' && ci[x][nx] == 'o')g[n * i + y].push_back(n * nx + x);
}
}
vector<bool>vis(n * w, 0), finish(n * w, 0);
for(int i = 0 ;i < n * w ;i ++)
{
if(dfs(g, vis , finish, i))
{
cout << "Yes\n";
return;
}
}
cout << "No\n";
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int _;cin >> _;
while(_--) solve();
return 0;
}
// bidirectional 双向
// certain days 某些天
#include <bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
int n,m, w;
bool dfs(vector<vector<int>>&g, vector<vector<bool>>&vis,vector<vector<bool>>&finish ,vector<string>&ci, int x, int date)
{
if(finish[x][date]) return false;
vis[x][date] = true;
for(auto v : g[x])
{
int y = (date + 1) % w;
if(ci[v][y] == 'x') continue;
if(finish[v][y]) continue;
if(vis[v][y] || dfs(g, vis, finish,ci, v,y)) return true;
}
finish[x][date] = true;
return false;
}
void solve()
{
cin >> n >> m;
vector<string>ci(n);
vector<vector<int>>g(n);
for(int i = 0;i < m ;i ++)
{
int x, y;cin >> x >> y;
-- x, -- y;
g[x].push_back(y);
g[y].push_back(x);
}
for(int i = 0; i < n ;i ++) g[i].push_back(i);
cin >> w;
for(int i = 0 ;i < n ;i ++) cin >> ci[i];
vector<vector<bool>>vis(n, vector<bool>(w, 0)), finish(n, vector<bool>(w, 0));
for(int i = 0 ;i < n ;i ++)
{
for(int j = 0;j < w ;j ++)
{
if(dfs(g, vis , finish, ci, i, j))
{
cout << "Yes\n";
return;
}
}
}
cout << "No\n";
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int _;cin >> _;
while(_--) solve();
return 0;
}
// bidirectional 双向
// certain days 某些天
最优解的思考
对于求一些最优解(就只是求一个数),一般考虑有dp,贪心(各种贪心如:反悔贪心...),和二分答案....
求错误指令数量思想
重点:指令取反
有些题目会给出一些指令,说会撒谎之类的,然我们给出最大错误指令数量等。
我们可以将其取反进行操作记录。最终反指令执行次数,就是我们错误指令的数量。
比如这个:
题目:牛牛的说谎机器
使得指令错误可以看作将指令含义取反,那么只需要维护 1, 2, ..., n 这些值分别处在多少个反区间内即可,最终答案取最大值,对最大值计数即可。(从其他佬那里学的)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6 + 10;
ll pre[N];
void solve()
{
int n, m;cin >> n >> m;
for(int i = 0 ;i < m ; i ++ )
{
int op;cin >> op;
if(op == 1)
{
int u, v;cin >> u >> v;
pre[1] ++ ,pre[u] -- ,pre[v + 1] ++;
}
else if(op == 2)
{
int u;cin >> u;
pre[1] ++ ,pre[u] --;
}
else if(op == 3)
{
int v;cin >> v;
pre[v + 1] ++;
}
}
ll mx{},res{};
for(int i = 1; i<= n ;i ++ )
{
pre[i] += pre[i- 1];
mx = max(pre[i], mx);
}
for(int i = 1 ;i<=n ; i++ ) res += (pre[i] == mx);
cout << mx << " " << res << '\n';
}
int main()
{
int _ = 1;
while( _ -- ) solve();
return 0;
}
方向构造思想
先假想构造出最优解形式,然后反推回去(往上补)
逆向思维的一种
例子Interval Mod
上下界思想
先想办法观察出题目的上下界,然后看一下上界到下界是否均可达,然后就可以用此算出最终合法答案数量
例子Maximum Rating
计算树上任意两点距离
可以采用LCA + 二进制倍增的方式计算,复杂度是\(O(logn)\)
具体代码
ll dist(int u, int v)
{
ll res{};
if(dis[u] > dis[v]) swap(u, v);
int dif = dis[v] - dis[u];
//0101
for(int i = 30; i >= 0 ;i -- )
{
if(dif >> i & 1) v = fa[v][i];
}
res += dif;
for(int i = 30 ;i >= 0 ;i -- )
{
if(fa[u][i] != fa[v][i])
{
u = fa[u][i];
v = fa[v][i];
res += 1LL << (ll)i << 1;
}
}
return res + (u != v) * 2;
}
例题小红的树染色
树的直径的计算
可以记点u,v和距离d,初始化u = v = 任意一个点,d = 0
然后不断添加点进去,添加点时候可以用用LCA + 二进制倍增去计算一下点之间的距离
而观察一下新增的点去更新一下u,v和d
具体代码
for(int& v : r)
{
int da = dist(a, v);
int db = dist(b, v);
if(max(da, db) < d) continue;
if(da >= db)
{//新的直径应该是av,应该更新b
b = v;
d = da;
}
else
{
a = v;
d = db;
}
}
例题小红的树染色
序列极大数和极小数限制统计
先给定义
- 广义峰 : 只有一边的也算峰(比如1 2 3 4 5,就认为有一个个广义峰)
- 狭义谷 : 认为两边都要全的才是谷(如3 2 1认为只有一个谷)
这里约定要求求n的排列中恰有k个侠义谷的排列数
这里有一个知识点就是:
对于一个序列插入最大值的时候插入在广义峰的峰顶两边,广义峰的数量不变,插入在其他地方广义峰数量 + 1,对于插入最小值,则是插入在广义谷两边不变其他 + 1
例题小红的排列计数

浙公网安备 33010602011771号