计数类问题

例1 luogu4630 [APIO2018] 铁人两项
建圆方树.
如果固定了s,f, 合法的c集合是圆方树上s->f路径上经过的点双的并.
给所有点赋权,方点点权为点双大小,圆点点权为-1.
那么合法的c数量便是s->f路径点权和.
问题变成了求所有圆点两两之间的路径点权和的和.
dfs(x)时,answer+=通过x的次数*val[x].
代码如下:

#include <bits/stdc++.h>

using namespace std;

#define ll long long 

inline int read() {
   
	int x = 0, f = 0; char ch = getchar();
	while (!isdigit(ch)) f = ch == '-', ch = getchar();
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}

const int N = 200010, M = 1100010; 
int n, m, rt; 
int dfn[N], low[N], num, cnt; 
int stk[N], top; 
bool cut[N]; 

vector<int> dcc[N]; 

struct Tree {
   
	int head[N], nex[M], ver[M], tot = 1; 
	void add(int x, int y) {
    ver[++tot] = y; nex[tot] = head[x]; head[x] = tot; }
};
Tree tr, yf; 

int siz[N]; 
ll val[N], answer; 

void tarjan(int x) {
   
	dfn[x] = low[x] = ++num; 
	stk[++top] = x; 
	int flag = 0, son = 0; 
	for (int i = tr.head[x]; i; i = tr.nex[i]) {
   
		int y = tr.ver[i]; 
		if (!dfn[y]) {
   
			++son; 
			tarjan(y); 
			low[x] = min(low[x], low[y]); 
			if (dfn[x] <= low[y]) {
   
				++flag; 
				if (flag > 1 || x != rt) cut[x] = 1; 
				++cnt; 
//				printf("x = %d, y = %d, ++cnt; \n", x, y); 
				int z; 
				do {
   
					z = stk[top--]; 
					dcc[cnt].push_back(z); 
				} while (z != y); 
				dcc[cnt].push_back(x);
			} 
		} else low[x] 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值