SDOI2018一轮NOI培训 题目整理

\(qwq\)首先,这些题对于我而言……类似于\(emmm\)洪水猛兽

\(\mathcal{Day \ \ 1}\)

T1

\(\mathcal{\color{red}{Description}}\)

作为钦钦草原最绿的男人,杨某针每天都要开车巡视钦钦草原一圈。钦钦草原由 \(n\) 个城市组成, \(m\) 条双向道路连接着它们。经过第 \(i\) 条道路要花费的
时间是 \(2^i\)

杨某针想要经过每条道路至少一次,在此基础上他想最小化他花费的时间。但作为曾经 \(CTSC\)\(Cu\) 选手,他并不能很快地计算出这个问题。所以他向你求助。

【输入格式】
从文件 \(carcar.in\) 中读入数据。

输入第一行包含两个正整数 \(n, m\)

接下来 \(m\) 行,每行两个正整数 \(a_i, b_i\),表示第 \(i\) 条边连接点 \(a_i\)\(b_i\),它的权值为 \(2^i\)

保证 \(a_i ,b_i\),不存在重边,且任意两个点之间可以互相到达。

【输出格式】
输出到文件 \(carcar.out\) 中。

输出一行一个整数,表示答案对 \(10^9 + 7\) 取模的值。

\(\mathcal{\color{red}{Solution}}\)

首先我们要认识到,这个题类似于求一个欧拉路这么一个东西。但是要求遍历所有的边并回到出发点,那么这不就是传说中的——一笔画问题吗qwq?

嗯,那就是欧拉图了!即,如果你想对一个图进行一笔画,那么首先你需要它是一个偶图,意思就是需要这个图上,度数为奇数的点的个数为0或者为2。我们再考虑,一条路径在最优情况下,至多通过两次,因为通过三次就等同于通过一次,以此类推。

那,首先我们需要对这张图进行重构,如果不是一张偶图的话,我们就要添一条边。那添边怎么添呢?当然是贪心地、单调地添边啦!所以不妨在最小生成树中选边。而对于这种贪心的方式,由于它的边权是2的次幂,所以因为\[\sum\limits_{i = 1}^{n - 1}{2^i} < 2^n\],可以保证贪心地添边是没错的,因为无论怎么添边,之后的一定没有之前的更优。

于是结束,撒花花!

至于代码,就是一遍\(dfs\)加上一次最小生成树,时间复杂度\(O(n + m)\),完全可以接受。有些有意思的小细节也是可圈可点的\(qwq\).

const int maxn=401000;
const int mod=1e9+7;
struct node{
    int point;
    int weight;
    int nxt;
};
node line[maxn<<1];
int head[maxn],tail;
int ans=0;
int deg[maxn];
void add(int a,int b,int c){
    line[++tail].point = b;
    line[tail].weight = c;
    line[tail].nxt = head[a];
    head[a] = tail;
}
int f[maxn];
int find(int x){
    return f[x] == x ? x : f[x] = find(f[x]) ;
}
bool _union(int a,int b){
    int f1 = find(a), f2 = find(b);
    if(f1 == f2)    return false;//无法合并 
     f[f1] = f2;    return true;//合并完返回 
}
void dfs(int now,int fa){
    for(int i=head[now];i;i=line[i].nxt)
        if(line[i].point!=fa)//遍历整颗生成树 
        {
            dfs(line[i].point,now);//向下递归解决子问题 
            if(deg[line[i].point])  deg[now]^=1,ans=(ans+line[i].weight)%mod;//如果儿子的度数为奇数,我们就补一条,然后自己的度数++ 
        }
    return ;
}
int main(){
    int now = 1,a,b,n,m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n;i ++) f[i] = i ;//并查集初始化 
    for(int i = 1; i <= m;i ++)
    {
        scanf("%d%d",&a,&b);
        now <<= 1;//now为边权 
        now = now % mod;
        ans = (ans + now) % mod ;//先将每条边都算一遍 
        if(_union(a,b)) add(a, b, now), add(b, a, now) ;//因为边权是有序的,所以可以直接跑 
        deg[a]^=1, deg[b]^=1;//度数问题我们只用关心奇偶就可以了 
    }
    dfs(1, 0) ;//其实从哪遍历都一样。 
    printf("%d", ans) ;//输出 
}

T2

\(\mathcal{\color{red}{Description}}\)

Ps:前不久因为我太颓了……所以这个就鸽了……ORZ我慢慢更……有些题不会我还要现学啊

转载于:https://www.cnblogs.com/pks-t/p/9175951.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值