D - Sum of Paths
题目链接
D题题意是给一个数组,刚开始随便在一个点,之后随机向左向右走。只能在
1
−
n
1 - n
1−n中走(不能越界),每走到一个点就加上这个位置的值,问走的所有路径的权值总和是多少。有m次查询,每次修改一个位置上的权值。
这题很显眼,就是统计每个点总过走多少次。(在所有的路径中出现的次数)于是可以想到
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示走
j
j
j步后到
i
i
i点的种类数,转移方程:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
−
1
]
+
d
p
[
i
+
1
]
[
j
−
1
]
dp[i][j] = dp[i - 1][j - 1] + dp[i + 1][j - 1]
dp[i][j]=dp[i−1][j−1]+dp[i+1][j−1].刚开始想的把
d
p
[
i
]
[
1
−
k
]
dp[i][1 - k]
dp[i][1−k]全加起来就是
i
i
i出现的个数,但是这不对!!!!!,漏了,漏了什么?例如刚开始的时候,
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]初始化为1,但是并不是1,因为之后的路径不一样。刚开始的得算好多次。。
于是怎么办? 看了题解。。。 发现
d
p
dp
dp数组也可以是还差
j
j
j步的时候在
i
i
i点。。。所以最后出现的个数是:
d
p
[
i
]
[
j
]
∗
d
p
[
i
]
[
k
−
j
]
,
j
∈
[
0
,
k
]
dp[i][j] * dp[i][k - j],j∈[0,k]
dp[i][j]∗dp[i][k−j],j∈[0,k].
。。。
#include<stdio.h>
#include<set>
#include<algorithm>
#include<vector>
#include <queue>
using namespace std;
#define mkp make_pair
#define st first
#define sd second
#define pb push_back
typedef long long ll;
typedef pair<int,int> pii;
const int inf = 0x3f3f3f3f;
const int maxn = 5000+105;
const ll mod = 1e9+7;
ll num[maxn];
ll dp[maxn][maxn];
ll a[maxn];
int main()
{
int n, k, m;
scanf("%d%d%d",&n,&k,&m);
for (int i = 1; i <= n; i ++ )
{
scanf("%lld",&a[i]);
}
for (int i = 1; i<= n; i ++ )
dp[i][0] = 1;
for (int i = 1; i <= k; i ++ )
{
for (int j = 1; j<= n; j ++ )
{
dp[j][i] = dp[j + 1][i - 1] + dp[j - 1][i - 1];
dp[j][i] %= mod;
}
}
for (int i= 1;i <= n; i ++ )
{
for (int j = 0; j <= k; j ++ )
{
num[i] += dp[i][j] * dp[i][k - j] % mod;
num[i] %= mod;
}
// printf("%d ",num[i]);
}
// printf("\n");
ll ans =0 ;
for (int i = 1; i <= n; i ++ )
{
ans = (ans + num[i] * a[i] % mod) % mod;
}
while(m -- )
{
int p;
ll x;
scanf("%d%lld",&p,&x);
ans += (x - a[p]) * num[p] % mod;
ans %= mod;
ans += mod;
ans %= mod;
a[p] = x;
printf("%lld\n", ans);
}
}
E. Distinctive Roots in a Tree
题目链接
题意:
给一个树,有点权。如果 从这个点到其他所有点的路径中没有重复的点权那么这个点就是好的,问有多少个点是好的。
刚开始想的:
如果两个点点权一样,那么这两个点的子树都不能是好的。但是没法实现。并且注意到:如果有3个点一样并且在一条路径上,那么所有点都不是好的。
但是如果换根dp,不知道dp要维护一些什么东西。。。
题解:
差分,把不好的点分为两类:
- 如果这个点的颜色在这个子树中出现了,那么除了这个子树,别的点都不是好的。
- 如果这个点的颜色没有全部包含在这个子树中,那这个子树都不是好的。
- 这两种情况可以覆盖所有不好的情况。
于是就简单了。。
因为都是子树的问题,所以直接在dfs序上差分就好。
#include<stdio.h>
#include<set>
#include<algorithm>
#include<vector>
#include <queue>
using namespace std;
#define mkp make_pair
#define st first
#define sd second
#define pb push_back
typedef long long ll;
typedef pair<int,int> pii;
const int inf = 0x3f3f3f3f;
const int maxn = 2e5+105;
const ll mod = 1e9+7;
int l[maxn];
int r[maxn];
int a[maxn];
std::vector<int> vv[maxn];
int pos = 0;
void dfs(int x,int fa)
{
l[x] = ++pos;
for(int i = 0; i < vv[x].size(); i ++ )
{
int v = vv[x][i];
if(v == fa) continue;
dfs(v,x);
}
r[x] = pos;
}
int num[maxn];
int s[maxn];
int cha[maxn];
void Dfs(int x,int fa)
{
int per = num[a[x]];
num[a[x]] ++ ;
for (int i =0 ; i < vv[x].size(); i ++ )
{
int v = vv[x][i];
if(v == fa)
continue;
int p = num[a[x]];
Dfs(v,x);
int q = num[a[x]];
if(q > p)
{
// 如果子树中有这个颜色 那么除了这个子树 别的都不能用
int lp = l[v];
int rp = r[v];
cha[1] ++ ;
cha[lp] -- ;
cha[rp + 1] ++ ;
}
}
if(num[a[x]] - per != s[a[x]])
{
// 除了这个子树 ,别的地方还有这个颜色,那这个子树不可用
cha[l[x]] ++ ;
cha[r[x] + 1] -- ;
}
}
std::vector<int> vp;
int main()
{
int n;
scanf("%d",&n);
for (int i= 1; i <= n; i ++ )
{
scanf("%d",&a[i]);
vp.pb(a[i]);
}
sort(vp.begin(),vp.end());
vp.erase(unique(vp.begin(),vp.end()),vp.end());
for (int i = 1; i <= n; i ++ )
{
a[i] = lower_bound(vp.begin(),vp.end(),a[i]) - vp.begin() + 1;
s[a[i]] ++ ;
}
for(int i = 1; i < n; i ++ )
{
int x,y;
scanf("%d%d",&x,&y);
vv[x].pb(y);
vv[y].pb(x);
}
dfs(1,0);
Dfs(1,0);
int ans =0 ;
for (int i = 1; i <= n; i ++ )
{
cha[i] += cha[i - 1];
if(cha[i] == 0)
ans ++ ;
}
printf("%d\n", ans);
}