165 F - LIS on Tree
题意:
一棵有 n 个结点的树,每个结点有一个值,输出每个从根节点 1 到其他点最短路的值序列中 LIS 的长度。
数据范围:
2 ≤ N ≤ 2 ×
1 ≤ ai ≤
1 ≤ ui , vi ≤ N
ui != vi
思路:
存好图之后dfs,相当于维护一个最长单调序列 b。
每次在 a 数组中遍历到一个新的节点,与此时的 b 数组已得到的LIS序列(单调递增的)的值判断后:如果该值大于 b 数组最后一个值,对该节点的值进行入队;否则找到第一个大于该值的位置在b 数组中进行替换操作,
在走完这个节点的子树之后,注意到树中不同链的LIS是互不影响的,所以我们回溯时需要还原,回溯就意味着我们需要改变 b 数组现存的LIS。因为这个子树中某些节点可能已经插入到数组 b中,我们要消除这个子树中节点对以后的影响,所以将 b 数组的长度减少;如果存在替换操作就把 b 数组还原。
Code:
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
#define int long long
#define LL long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 200010, INF = 0x3f3f3f3f, mod = 1e9 + 7;
typedef pair<int, int>PII;
int a[N], ans[N]; //a数组存每个节点的权值,ans数组存答案
int b[N], len; //b数组存此时遍历到的点的路径中的LIS,len表示此时的LIS长度
vector<int>e[N]; //e二维数组存边
void dfs(int u, int pre) //u表示当前节点,pre表示当前节点的父节点
{
int id = lower_bound(b, b + len, a[u]) - b; //在b数组的LIS序列中找到值大于等于a[u]的位置
int del = -1; //del用于判断是否出现需要在已有数的b数组LIS中替换数
if (id == len)len++; //如果id和len相等,说明a[u]的值比b数组的值都大,接在b数组后面即为新的LIS
else del = b[id]; //如果不相等,说明该节点u之前的节点值大于a[u],不能在现有的LIS序列中接a[u],需要替换,所以先用del记录下原来b数组中id位置的值
b[id] = a[u]; //将b数组中id位置更新为a[u]
ans[u] = len; //此时从节点1到节点u的LIS即为len
for (auto v : e[u])
if (v != pre)
dfs(v, u); //dfs递归计算出所有节点的LIS
if (del == -1)--len; //该子树遍历完后,即该层递归结束后,需要回溯
else b[id] = del; //用del还原b数组id位置的值
}
void solve()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)cin >> a[i]; //输入树中每个节点的权值
for (int i = 0; i < n - 1; i++)
{
int u, v;
cin >> u >> v;
u--, v--;
e[u].push_back(v); //二维数组存边
e[v].push_back(u);
}
dfs(0, -1); //DFS找出每个从根节点 1 到其他点最短路的值序列中LIS的长度
for (int i = 0; i < n; i++)
cout << ans[i] << endl;
}
signed main()
{
IOS;
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
163 F- path pass i
题意:
给一棵 N 个节点的无根树,每个节点有一个值 c,该值表示每个节点的颜色,数值相同说明颜色相同,对于每个颜色,求经过这种颜色的简单路径的数量。
数据范围:
1 ≤ N ≤ 2 ×
1 ≤ ci ≤ N
1 ≤ ai, bi ≤ N
思路:
如果算包含一种颜色的点的路径,是很困难的。(1-2-3包含2,2-3也包含2,2也包含2,1-2也包含……要是点的数目大,似乎无规律可循)
算一个连通块的路径的数目,就是 n*(n-1)/2(排列组合Cn2),注意在这道题中,只有一个点也算一条路径,那么加上 n,得到算一个连通块的路径的数目为 n*(n+1)/2 。那么问题就可以转化为,求“路径总数 - 不包含颜色为i的点的所有连通块内部的路径数”。
接下来就只用找出不包含每种颜色的所有连通块的大小————
树形DFS,一遍把树全部遍历O(n)即可;
实现:
1. 用二维数组存树
2. 纵向DFS:一直DFS到最底层之后,回溯从叶子往根走的过程中,每遇到一种颜色,马上计算上一次(也是在回溯过程中)遇到的这种颜色离这里的位置,就可以得出之间并非此颜色的节点数目。更新一次答案。把这种颜色的最近位置更新;
那么回溯到根节点的时候,除了根节点颜色以外的其他颜色都没有进行最后一次更新(根附近的并非此颜色的连通块没有被计算),这就在DFS结束之后补上。
3. 横向DFS:因为是树嘛,可能不是一条直链,而有很多分链,DFS的时候就要考虑到这些分链的作用。于是每到DFS一个节点的时候,都要记录当前的数目,每次进入DFS之前,记录一下,回来之后,计算一下改变量。
这是整体思路,具体代码实现还是有亿点点细节处理。理解不深,写不出来细节怎么实现的……
Code:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define x first
#define y second
#define int long long
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };
const int N = 200010, INF = 0x3f3f3f3f, mod = 1e9 + 7;
typedef pair<int, int>PII;
int a[N], siz[N], sum[N]; //siz[i]表示i的子树(包括i)的节点个数,sum[i]代表以颜色为i的节点(其祖先没有颜色为1的节点)为根节点的子树节点中颜色为i的节点个数和
vector<int>G[N];
int ans[N];
int cal(int x)
{
return x * (x + 1) / 2; //算一个连通块的路径的数目,即n*(n-1)/2;该题中,一个点也算一条路径,加上n,得到算一个连通块的路径的数目为n*(n+1)/2
}
void dfs(int u, int fa)
{
//int c = a[u], save = sum[c];
siz[u] = 1;
int pre = sum[a[fa]], t = sum[a[u]]; //这个pre和t都是遍历到u之前的
for (auto &v : G[u])
{
if (v == fa)continue;
dfs(v, u);
siz[u] += siz[v]; //更新节点u子树大小
}
sum[a[u]] = t + siz[u];
ans[a[fa]] -= cal(siz[u] - (sum[a[fa]] - pre));
}
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i]; //输入n个点的颜色
for (int i = 1; i <= n - 1; i++)
{
int u, v;
cin >> u >> v;
G[u].push_back(v); //存边
G[v].push_back(u);
}
for (int i = 1; i <= n; i++)
ans[i] = cal(n); //初始值为n个点的连通块的总路径数
dfs(1, 0);
for (int i = 1; i <= n; i++)
{
ans[i] -= cal(n - sum[i]); //把离根最近的连通块补上
cout << ans[i] << endl;
}
}
signed main()
{
//int t;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
吐槽:两题都用到了DFS树,所以就写一起了,不过后一个题难度上了一个层次,在DFS的基础上让我感觉更难理解了orz,虽然看半天就看懂了七七八八,但是还是放上吧,说不定以后能完全看懂的说不定。