2017 10 09 NOIP2017模拟赛

最后一题5分很爆炸。。。慎用切分。。。


双击 2923

日常一水题,记录一下左右边界就好了


取子串 2991

DP题 取子串


Paths 2954

一道关于树的图论

先来考虑一条链的情况(考试时切分把链的分都切没了。。。)
不难发现在一条链上本题就变成一道很经典的贪心了
只要根据右端点排序就可以直接贪心

其实在树上也是差不多的思路
正解是根据添加的链的两个端点的LCA的深度从大到小来排序

貌似正确性还是挺显然的
因为当我们选取一条链的时候,这两个点的LCA的子树就没掉了
那如果这个子树中存在其他合法的链怎么办
如果存在这种链,那么它的LCA深度显然要更深,必然优先考虑

选取一条链后,要将它的子树都mark掉
接下来只要判一条链的两端是否被mark就行了
这个mark的操作显然不会超时
因为每个点最多的被mark一次

这道题还有一道升级版。。。
就不是贪心这么简单了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;

#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
#define INF 0x3f3f3f3f
#define M 100005
#define N 20

void chkmx(int &a,int b) {if(a<b)a=b;}
void chkmi(int &a,int b) {if(a>b)a=b;}
int MAX(int a,int b) {return a>b?a:b;}
int MIN(int a,int b) {return a<b?a:b;}

struct edge {
    int fr,to,lca;
} G[M];
vector<int> es[M];
int n,m;
int dep[M],Fa[M][N];
bool cmp(edge a,edge b){
    return dep[a.lca]>dep[b.lca];
}
struct _LCA_{
    void dfs(int s,int fr) {
        dep[s]=dep[fr]+1,Fa[s][0]=fr;
        FOR(i,0,es[s].size()-1) {
            int y=es[s][i];
            if(y==fr) continue;
            dfs(y,s);
        }
    }
    void Init(){
        dfs(1,0);
        FOR(j,1,N-1)FOR(i,1,n)
            Fa[i][j]=Fa[Fa[i][j-1]][j-1];
    }
    void up(int &s,int step){
        FOR(i,0,N-1){
            if((1<<i)&step)
                s=Fa[s][i]; 
        }
    }
    int LCA(int a,int b){
        if(dep[a]>dep[b]) swap(a,b);
        up(b,dep[b]-dep[a]);
        if(a==b) return a;
        DOR(i,N-1,0){
            if(Fa[a][i]!=Fa[b][i])
                a=Fa[a][i],b=Fa[b][i];
        }
        return Fa[a][0];
    }

    void solve(){
        FOR(i,1,m) G[i].lca=LCA(G[i].fr,G[i].to);
        sort(G+1,G+m+1,cmp);
    }
}LCA;
struct Solve{
    bool mark[M];
    int a,b,ans;
    void Input(){
        cin>>n>>m;
        FOR(i,2,n) {
            scanf("%d%d",&a,&b);
            es[a].push_back(b);
            es[b].push_back(a);
        }
        FOR(i,1,m)scanf("%d%d",&G[i].fr,&G[i].to);
    }
    void dfs(int s,int fr){
        if(mark[s]) return ;
        mark[s]=1;
        FOR(i,0,es[s].size()-1){
            int y=es[s][i];
            if(y==fr) continue; 
            dfs(y,s);
        }
    }
    void work(){
        ans=0;
        FOR(i,1,m){
            if(mark[G[i].fr]||mark[G[i].to]) continue;
            dfs(G[i].lca,Fa[G[i].lca][0]);
            ans++;
        }
        cout<<ans<<endl;
    }
}Solve;
int main() {
    Solve.Input();
    LCA.Init();
    LCA.solve();
    Solve.work();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值