remenber who you fighting for
Problem C Tree
在所有的黑点里选m个点,让这m个点之间最长的距离最短。
应该是个二分,训练的时候没有想出来怎么check。
其实check用tree dp来搞一下。我们定义dp[i][j]是以i节点为根,最长距离不超过j情况下,最多能选出来的黑点个数。也就是说我们最后check一下有没有dp[i][mid] >= m的情况,如果有的话就认为可以继续减少。
更新是这样的:
1.dp[u][i] = max(dp[u][i],dp[u][i-1]) 相当于更新上一个长度的值
2.距离是有限的,我们需要对更新的最长的长度做一个限制,现在我们已经离根节点距离是j了,那么就只能走min(m-j-1,j-1)步了。对于这个限制,如果能更新一个子节点,相当更新加上dp[v][i-1]和这个子节点不相同的其他子节点的值dp[v][mn]的值。这个套路在tree dp离比较常见,相信大家比较好理解。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 300;
int c[MAXN];
int dp[MAXN][MAXN];
vector<int> edge[MAXN];
vector<int> in;
int n,m;
void dfs(int u,int p,int mid)
{
for(int v : edge[u])
{
if(v == p) continue;
dfs(v,u,mid);
}
if(c[u]) dp[u][0] = 1;
for(int i = 1;i<=mid;i++)
{
int mx = min(mid-i-1,i-1),sum = 0;
dp[u][i] = max(dp[u][i],dp[u][i-1]);
if(mx >= 0)
{
for(int v : edge[u])
{
if(v != p) sum += dp[v][mx];
}
}
for(int v : edge[u])
{
if(v != p)
{
int tmp = dp[v][i-1];
if(mx>=0) tmp += sum - dp[v][mx];
dp[u][i] = max(dp[u][i],c[u]+tmp);
}
}
}
}
bool check(int mid)
{
//for(int i= 0;i<MAXN;i++) for(int j =0;j<MAXN;j++) dp[i][j] = 0;
memset(dp,0,sizeof(dp));
dfs(1,-1,mid);
for(int i = 1;i<=n;i++)
{
if(dp[i][mid] >= m) return 1;
}
return 0;
}
int main()
{
cin>>n>>m;
for(int i = 1;i<=n;i++)
{
cin>>c[i];
}
for(int i = 0;i<n-1;i++)
{
int u,v;
cin>>u>>v;
edge[u].push_back(v);
edge[v].push_back(u);
}
int l = 0,r = n,ans = 0;
while(l <=r )
{
int mid =(l+r)>>1;
if(check(mid))
{
ans = mid;
r = mid-1;
}
else
{
l = mid+1;
}
}
cout<<ans<<endl;
return 0;
}
E. Fishermen
这个题还是很有意思的,我们需要针对每个a,求出满足
∣
x
i
−
a
∣
+
y
≤
l
|x_i-a|+y\le l
∣xi−a∣+y≤l的个数。
看到了一个不等式,我们想到了各种数据结构+不等式的题,我们就来拆一下这个式子。
a
≤
l
−
x
+
y
,
a
≥
x
+
y
−
l
a \le l-x+y,a \ge x+y-l
a≤l−x+y,a≥x+y−l
好了,发现是个左右界,给a数组离散化。然后对离散化之后的点区间加,单点查询就行
但是这个问题不涉及到修改,那我们直接差分一下就好了
#include <bits/stdc++.h>
#include <cstring>
using namespace std;
const int MAXN = 2e5 + 5;
int sum[MAXN],x[MAXN],y[MAXN],a[MAXN],b[MAXN];
map<int,int> pos;
int main()
{
int n,m,l;
scanf("%d%d%d",&n,&m,&l);
for(int i = 1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
}
for(int i = 1;i<=m;i++)
{
scanf("%d",&a[i]);
b[i] = a[i];
}
sort(a+1,a+1+m);
for(int i = 1;i<=m;i++)
{
pos[a[i]] = i;
}
for(int i = 1;i<=n;i++)
{
int L = -l+x[i]+y[i];
int R = l+x[i]-y[i];
if(L > R) continue;
int lid = lower_bound(a+1,a+m+1,L) - (a);
int rid = upper_bound(a+1,a+m+1,R) - (a);
//cout<<L<<' '<<R<<' '<<lid<<' '<<rid<<endl;
sum[lid]++,sum[rid]--;
}
for(int i = 1;i<=m;i++) sum[i] = sum[i-1] + sum[i];
for(int i =1;i<=m;i++)
{
printf("%d\n",sum[pos[b[i]]]);
}
return 0;
}