Codeforces Gym 101612 | 2017-2018 NEERC St Petersburg Subregional

居然这套题要文件输入输出。。

A: Auxiliary Project

题目大意

Anna的机器可以显示最多n条灯条,问显示n条灯条能表示的数字的和最大是多少。

题解

因为n不大,所以完全背包即可。

#include <cstdio>
#include <algorithm>
using namespace std;

const int s[] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};

int n;
int f[1000005];

int main() {
    freopen("auxiliary.in", "r", stdin);
    freopen("auxiliary.out", "w", stdout);
    scanf("%d", &n);

    for (int i = 1; i <= n; ++i) f[i] = -1;
    f[0] = 0;

    for (int i = 0; i < 10; ++i)
        for (int j = s[i]; j <= n; ++j)
            if (f[j - s[i]] >= 0)
                f[j] = max(f[j], f[j - s[i]] + i);

    printf("%d\n", f[n]);

    return 0;
}

B: Boolean Satisfiability

略。。

C: Consonant Fencity

题目大意

给定长度不超过 10 6 个字符的只有小写字母构成的字符串,要求将某一些字母改为大写,使得字符串中连续的两个字符都为辅音且大小写不同的组数最多。
特别地,a,e,i,o,u,w和y算作元音。

题解

由于只有19个辅音字母,所以我们枚举这些字母是大写还是小写的即可。最开始预处理出来各个辅音字母在字符串中连续的组数即可。

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(i=j;i<k;++i)

const char consoants[19] = {'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'x', 'z'};

const int N = 1000005;

int len;
char s[N];
int f[20][20], g[20][20];
bool upper[128];

int main() {
    int i, j, ans = 0, tot, mx;
    freopen("consonant.in", "r", stdin);
    freopen("consonant.out", "w", stdout);

    scanf("%s", s);
    for (i = 0; i + 1 < s; ++i)
        ++g[s[i] - 'a'][s[i + 1] - 'a'];
    rep(i,0,19) rep(j,0,19)
        f[i][j] = g[consoants[i]][consoants[j]];
    for (int state = 0; state < (1 << 19); ++state) {
        tot = 0;
        rep(i,0,19) rep(j,0,19)
            if (((state >> i) ^ (state >> j)) & 1)
                tot += f[i][j];
        if (tot > mx)
            mx = tot, ans = state;
    }

    rep(i,0,19) if (ans & (1 << i)) upper[consoants[i]] = 1;
    for (i = 0; s[i]; ++i) putchar(upper[s[i] - 'a'] ? s[i] - 'a' + 'A' : s[i]);
    return 0;
}

E: Equal Numbers

题目大意

一个数列,每次操作将一个数字乘上一个正整数,对于 k [ 0 , n ] ,求 k 次修改后数列里不同的数字的种类数最少可以是多少。

分析

显然修改 n 次后必为1。
乘上一个数,会有两种结果,一个是倍数在数列中已经存在,那么直接改到倍数会比较优;一个是倍数在数列中不存在,将数字倍乘到数列的最小公倍数显然最优。两种情况取最小即可。

#include <cstdio>
#include <map>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6;

int f[N + 100];
map<int, int> mp;
vector<int> v1, v2;

int main() {
    freopen("equal.in", "r", stdin);
    freopen("equal.out", "w", stdout);

    int n, x, i, tot;
    scanf("%d", &n);

    for (i = 0; i < n; ++i) {
        scanf("%d", &x);
        ++mp[x];
    }

    for (auto entry : mp) {
        x = entry.first;
        v1.push_back(entry.second);
        for (i = x + x; i <= N; i += x)
            if (mp.count(i)) {
                v2.push_back(entry.second);
                break;
            }
    }

    memset(f, 0x3f, sizeof f);
    f[0] = tot = mp.size();

    int change = 0;
    sort(v2.begin(), v2.end());
    for (i = 0; i < v2.size(); ++i) {
        change += v2[i];
        f[change] = min(f[change], tot - i - 1);
    }

    change = 0;
    sort(v1.begin(), v1.end());
    for (i = 0; i < v1.size(); ++i) {
        change += v1[i];
        f[change] = min(f[change], tot - i);
    }

    for (i = 0; i <= n; ++i) {
        if (i) f[i] = min(f[i], f[i - 1]);
        printf("%d ", f[i]);
    }

    return 0;
}

F: Fygon 2.0

题目大意

至多20个循环变量,每个变量有枚举的范围,边界可以是外层循环的变量、常数,或者是n。计算时间复杂度(要求计算常数)

分析

对于每个变量 b ,有约束 a b c 。由于问题相似我们归纳为一些不等式约束 a b 。剔除掉约束后变量只能取一个值的情况,我们可以根据不等式建立拓扑图。问题转化为求拓扑图的拓扑序个数。利用状压DP解决。

G: Grand Test

题目大意

给一个无向图,判断是否存在两个点使得这两个点有三条不相交的路径相连。

题解

跑出DFS树后如果存在两条返祖边即可。
或者求出双连通分量后,分量内有环,如果环上有2个点的度数>=3,那么这两个点就是所求。

H: Hidden Supervisors

题目大意

给定有根森林,问如何连边使得最后的树的匹配数最大。

题解

由于是树,因此自底向上贪心地匹配就可以达到最大匹配。然后对于森林中的一些树,我们可以将这些没被匹配到的根连到其他未匹配的节点达成新的匹配。如果根已匹配过连到1即可。

I: Intelligence in Perpendicularia

题目大意

给出一个只有水平和竖直边,且顶点在整数坐标点上的多边形,求不在最外层边的长度。
(最外层边指水平或竖直的直线切多边形切到的边序列非首尾的元素。怎么表达起来更复杂了。。)

题解

考虑到最外层的边一定可以向外移动,使得这些最外层的边组成一个矩形,那么剩下的就是不在最外层的边了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1005;
ll dist(ll x1, ll y1, ll x2, ll y2) {
    if (x1 == x2) return abs(y2 - y1);
    return abs(x1 - x2);
}
ll x[N], y[N];
int main() {
    freopen("intel.in", "r", stdin);
    freopen("intel.out", "w", stdout);

    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++)
        scanf("%lld%lld", &x[i], &y[i]);

    ll minX = x[0], minY = y[0], maxX = x[0], maxY = y[0], len = 0;
    for (int i = 0; i < n; i++) {
        len += dist(x[i], y[i], x[(i + 1) % n], y[(i + 1) % n]);
        minX = min(minX, x[i]);
        maxX = max(maxX, x[i]);
        minY = min(minY, y[i]);
        maxY = max(maxY, y[i]);
    }
    printf("%lld", len - 2 * (maxX - minX + maxY - minY));

    return 0;
}

K: Kotlin Island

题目大意

w × h 的地图用一整列或一整行分割成 n 的部分。

题解

往地图中填'.''#'交替的部分,剩下的用'#'填起来就好了。
比如这样:

.#.#.#####
##########
.#.#.#####
##########
##########
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
char map[128][128];

int main() {
    int H, W, n;
    bool transposed = false;

    freopen("kotlin.in", "r", stdin);
    freopen("kotlin.out", "w", stdout);

    scanf("%d%d%d", &H, &W, &n);

    if (H > W) {
        swap(H, W);
        transposed = true;
    }

    for (int i = 1; i * i <= n; ++i)
        if (n % i == 0) {
            int h = i, w = n / i, x, y, t;

            if (2 * h - 1 > H || 2 * w - 1 > W)
                continue;

            if (transposed) {
                swap(H, W);
                swap(h, w);
            }

            memset(map, '.', sizeof(map));

            for (x = 2, t = 1; t < h; x += 2, ++t)
                for (y = 1; y <= W; ++y)
                    map[x][y] = '#';
            for (; x <= H; ++x)
                for (y = 1; y <= W; ++y)
                    map[x][y] = '#';

            for (x = 2, t = 1; t < w; x += 2, ++t)
                for (y = 1; y <= H; ++y)
                    map[y][x] = '#';
            for (; x <= W; ++x)
                for (y = 1; y <= H; ++y)
                    map[y][x] = '#';

            for (x = 1; x <= H; ++x) {
                for (y = 1; y <= W; ++y)
                    printf("%c", map[x][y]);
                puts("");
            }

            return 0;
        }

    return puts("Impossible"), 0;
}

L: Little Difference

题目大意

n 分解成一些数的乘积,这些数的差的绝对值不超过1,比如12可以分解为2*2*3但不能分解为2*6。
如果能无限分解输出-1。

题解

无限分解的情况就是无穷多个1相乘,因此只有 2 k 能无限分解(2和1的差为1不超过1)。
剩下就只有 n = a k n = a p × ( a + 1 ) q 两种情况。注意到 k , p , q 60 (因为 2 60 > 10 18 ),我们可以枚举指数,然后二分底数。乘积可能爆long long,需要注意一下。

#include <cstdio>
#include <vector>
#include <functional>
using namespace std;
typedef long long ll;
#define FOR(i,j,k) for(int i=j;i<=k;++i)
const ll LL_MAX = 1e18 + 100ll;

ll n;

ll mul(ll a, ll b) {
    if (a > LL_MAX / b) return LL_MAX;
    return a * b;
}

ll quick_pow(ll a, ll b) {
    ll ans = 1;
    for (; b; b /= 2, a = mul(a, a))
        if (b & 1) ans = mul(ans, a);
    return ans;
}

ll solve(function<int(int)> get_value) {
    ll l = 2, r = n, mid;
    while (l <= r) {
        mid = l + r >> 1;
        ll t = get_value(mid);
        if (t == n) return mid;
        if (t < n) l = mid + 1;
        else r = mid - 1;
    }
    return -1;
}

vector<vector<ll>> ans;

int main() {
    freopen("little.in", "r", stdin);
    freopen("little.out", "w", stdout);

    scanf("%lld", &n);
    if (n == 1 || (n & -n) == n)
        return puts("-1"), 0;

    FOR(k,1,60) {
        ll t = solve([k](int mid){return quick_pow(mid,k);});
        if (t == -1) continue;
        vector<ll> res;
        FOR(x,1,k) res.push_back(t);
        ans.push_back(res);
    }

    FOR(p,1,60) FOR(q,1,60) {
        ll t = solve([p, q](int mid){return mul(quick_pow(mid, p), quick_pow(mid + 1, q));});
        if (t == -1) continue;
        vector<ll> res;
        FOR(x,1,p) res.push_back(t);
        FOR(x,1,q) res.push_back(t + 1);
        ans.push_back(res);
    }

    printf("%d\n", ans.size());
    for (auto way : ans) {
        printf("%d", way.size());
        for (auto node : way)
            printf(" %lld", node);
        printf("\n");
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/huanghongxun/article/details/79383268
今日推荐