test20190731 夏令营NOIP训练16

0+90+0=90。我只挑了T2做。

连接格点

有一个M行N列的点阵,相邻两点可以相连。一条纵向的连线花费一个单位,一条横向的连线花费两个单位。某些点之间已经有连线了,试问至少还需要花费多少个单位才能使所有的点全部连通。

输入格式:
第一行输入两个正整数m和n。
以下若干行每行四个正整数x1,y1,x2,y2,表示第x1行第y1列的点和第x2行第y2列的点已经有连线。输入保证|x1-x2|+|y1-y2|=1。

输出格式:
输出使得连通所有点还需要的最小花费。

样例输入:
2 2
1 1 2 1

样例输出:
3

数据范围:
m,n<=1000

时间限制:
1000
空间限制:
32768

最小生成树,而且不用排序了。

无序字母对

给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒)。请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现。

输入格式:
第一行输入一个正整数n。
以下n行每行两个字母,表示这两个字母需要相邻。

输出格式:
输出满足要求的字符串。
如果没有满足要求的字符串,请输出“No Solution”。
如果有多种方案,请输出前面的字母的ASCII编码尽可能小的(字典序最小)的方案

样例输入:
4
aZ
tZ
Xt
aX

样例输出:
XaZtX

数据范围:
不同的无序字母对个数有限,n的规模可以通过计算得到。

时间限制:
1000
空间限制:
32768

题解

题意很清晰了,显然是求字典序最小的欧拉回路方案,但是我都忘了欧拉回路这回事。

手动模拟

发现合法情况一定是度数为奇数的点的个数为0或2。如果是0那就找最小的,如果是2那就找度数为奇数的最小的。

然后发现中间合并两个无序对,度数的变化是偶数2,所以一定有解。然后用set维护找字典序最小的即可。

但是交到洛谷1341发现WA了。然后看数据,发现了这种情况:

5
ax
xx
xt
tp
ps

也就是自环。在ax的时候x优先选择走t,但是之后就走不到x了。

所以在选择字典序较小的之前还要判断x下一个是否必须走到自己。这个就把其他点连回x的边数考虑一下就好了,把x放到后面边数至少为3。

最后还是有一个点莫名RE,而学军中学的数据好像是一样的,所以这题就是90分了。

#include<bits/stdc++.h>
#define co const
#define il inline
template<class T> T read(){
    T x=0,w=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*w;
}
template<class T>il T read(T&x){
    return x=read<T>();
}
using namespace std;

co int N=52;
il int idx(char c){
    return islower(c)?26+c-'a':c-'A';
}
il char inv(int i){
    return i<26?i+'A':i-26+'a';
}

int n,c[N];
multiset<int> s[N];
multiset<int>::iterator it;
int main(){
    read(n);
    for(int i=1;i<=n;++i){
        char p[3];scanf("%s",p);
        ++c[p[0]=idx(p[0])],++c[p[1]=idx(p[1])];
        s[p[0]].insert(p[1]),s[p[1]].insert(p[0]);
    }
    int cnt=0;
    for(int i=0;i<N;++i)
        if(c[i]&1) ++cnt;
    if(cnt!=0&&cnt!=2) return puts("No Solution"),0;
    char ans[3000];
    if(cnt==0){
        for(int i=0;i<N;++i)
            if(c[i]>=2){ans[0]=i,c[i]-=1;break;}
    }
    else{
        for(int i=0;i<N;++i)
            if(c[i]&1){ans[0]=i,c[i]-=1;break;}
    }
    for(int i=1;i<=n;++i){
        if(s[ans[i-1]].count(ans[i-1])){ // edit 1
            int cnt=0;
            for(int j=0;j<N;++j)if(j!=ans[i-1])
                cnt+=s[j].count(ans[i-1]);
            if(cnt<3){
                ans[i]=ans[i-1],c[ans[i]]-=2;
                s[ans[i]].erase(s[ans[i]].find(ans[i])),s[ans[i]].erase(s[ans[i]].find(ans[i]));;
                continue;
            }
        }
        int cnt=0;
        it=s[ans[i-1]].begin();
        if(i<n&&c[*it]==1) ++it;
        ans[i]=*it,c[ans[i]]-=2; // i==n doesn't matter
        s[ans[i-1]].erase(s[ans[i-1]].find(ans[i])),s[ans[i]].erase(s[ans[i]].find(ans[i-1]));
    }
    for(int i=0;i<=n;++i) ans[i]=inv(ans[i]);
    ans[n+1]=0;
    puts(ans);
    return 0;
}
/*
5
ax
xx
xt
tp
ps
*/

欧拉回路

欧拉回路算法直接就可以解决这个问题。

欧拉回路是在回溯的时候把路径的答案加进去。这样在x之后如果能回来,先走的就是字典序较小的;如果不能,先走的就是x。

感觉理解深入了不少。时间复杂度\(O(n \log n)\)

数字转换

如果一个数x的约数和(不包括它本身,下同)比它本身小,那么x可以变成它的约数和;如果对于某个y>x且y的约数和为x,那么x也可以变成y。例如,4可以变为3,1可以变为7。限定所有的数字变换在不超过n的正整数范围内进行,求不断进行数字变换且没有重复数字出现的最多变换步数。

输入格式:
输入一个正整数n。

输出格式:
输出最少需要花费的时间。

样例输入:
7

样例输出:
3

数据范围:
n<=50 000。

时间限制:
1000
空间限制:
32768

题解

只能向较小的连无向边,所以最后图一定是森林。

然后跑树的直径就可以了。

可以用来练习线性筛求约数和(误)。

线性筛的第二重循环中要筛出来的数\(x=i\times p_j\)最小质因子一定是现在枚举的\(p_j\),此时分为两种情况:

  • \(i\)没有质因子\(p_j\),那么直接计算即可。
  • \(i\)有质因子\(p_j\),那么应该修改\(p_j\)的贡献。所以需要记录每个数的最小质因子对它的约数和的贡献。

然后时间复杂度\(O(n)\)

猜你喜欢

转载自www.cnblogs.com/autoint/p/11275585.html