SEERC 2018 一些题解

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 xia+yl的个数。
看到了一个不等式,我们想到了各种数据结构+不等式的题,我们就来拆一下这个式子。
a ≤ l − x + y , a ≥ x + y − l a \le l-x+y,a \ge x+y-l alx+y,ax+yl
好了,发现是个左右界,给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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值