随手的笔记

点性质的思考

对于图中点的状态我们不只是可以通过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
    例题小红的排列计数
posted @ 2026-05-05 21:03  Mikan_QWQ  阅读(14)  评论(0)    收藏  举报