Codechef April Challenge 2019 Division 2

 

Maximum Remaining

题意:给n个数,取出两个数$a_{i}$,$a_{j}$,求$a_{i}\% a_{j}$取模的最大值

直接排个序,第二大(严格的第二大)模第一大就是答案了。

#include 
using namespace std;

int a[(int)1e6];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> a[i];
    sort(a, a + n, greater<int>());
    for (int i = 1; i < n; i++)
        if (a[i] != a[0]) {
            cout << a[i] % a[0] << '\n';
            return 0;
        }
    cout << "0" << '\n';
    return 0;
}
View Code

 

Friend or Girlfriend

题意:给一个字符串还有一个字母,问字符串里有多少个子串是这个字母

对于字符串 abcbcafdcde 给定字母为c

首先每到一个位置i 如果s[i]是c 答案就加上这个位置 表示以s[i]为结尾的子串

比如到s[3]是c 答案就加3 分别是abc bc c

到了s[5] 答案加上5 明显不止这么少

因为中间的b可以作为子串结尾 然后以位置3以前的任意一个字母为起点都是一个合法的子串

所以记录一下last的位置 ans += (i - last - 1) * last 就好了

因为最后一个位置不是给定字母的话不会被统计进去 特判一下就好了

#include 
using namespace std;

char s[(int)1e6 + 10];

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int n;
        scanf("%d", &n);
        scanf("%s", s + 1);
        char x[3];
        scanf("%s", x);
        long long ans = 0;
        int last = 0;
        for (int i = 1; i <= n; i++) {
            if (s[i] == x[0]) {
                ans += (long long)i;
                if (i - last - 1 != 0) {
                    ans += (long long)(i - last - 1) * last;
                }
                last = i;
            }
            if (i == n && s[i] != x[0]) {
                ans += (long long)(i - last) * last;
            }
        }
        printf("%lld\n", ans);
    }
}
View Code

 

Fencing

题意:给一个$N\times M$的矩阵,有 K 格是菜,求最少用多长的篱笆能把这些菜围起来(不能有边与边界或杂草相连)N,M都是1e9

最多肯定就是4 * K啦。然后连通的两个菜格子答案就-2

我是用了结构体和map来映射一个点的下标 然后dfs搜K个格子 只搜右和下两个方向 否则就会因为环跑不出来或者答案统计不全

#include 
using namespace std;

const int maxn = 1e5 + 10;
int n, m, k, ans;
struct Point {
    int x, y;
    bool operator < (const Point &a) const {
        if (x == a.x) return y < a.y;
        return x < a.x;
    }
} p[maxn];
mapint> mp;
bool vis[maxn];
void dfs(int x, int y, int index) {
    vis[index] = true;
    Point temp;
    temp.x = x + 1, temp.y = y;
    if (mp.count(temp)) {
        ans -= 2;
        if (!vis[mp[temp]]) dfs(x + 1, y, mp[temp]);
    }
    temp.x = x, temp.y = y + 1;
    if (mp.count(temp)) {
        ans -= 2;
        if (!vis[mp[temp]]) dfs(x, y + 1, mp[temp]);
    }
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        mp.clear();
        memset(vis, 0, sizeof vis);
        scanf("%d%d", &n, &m);
        scanf("%d", &k);
        for (int i = 1; i <= k; i++){
            scanf("%d%d", &p[i].x, &p[i].y);
            mp[p[i]] = i;
        }
        int index = 0;
        ans = 4 * k;
        for (int i = 1; i <= k; i++) {
            if (!vis[i])
                dfs(p[i].x, p[i].y, i);
        }
        printf("%d\n", ans);
    }
    return 0;
}
View Code

 

Subtree Removal

题意:给一棵带权的树和一个值X,可以执行k次操作,每次选择一个节点,删去它与它的子树,求剩下的节点权值和 - k * X

当时想的很复杂,不知道怎么写。

现在一想,其实是被这个k给限制住了,把k个X给拆出来,就相当于可以把k个节点包括它的子树的权值和给替换成 -X

这样想就是很简单了。(我好菜啊...

#include 
#define ll long long
using namespace std;

const int maxn = 1e5 + 10;
vector<int> G[maxn];
int n;
ll x;
ll a[maxn];
ll f[maxn];

void dfs(int u, int fa) {
    f[u] = a[u];
    for (int v : G[u]) {
        if (v == fa) continue;
        dfs(v, u);
        f[u] += f[v];
    }
    f[u] = max(f[u], -x);
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &x);
       // for (int i = 1; i <= n; i++) G[i].clear();
        for (int i = 1; i <= n; i++) {
            G[i].clear();
            scanf("%lld", &a[i]);
         //   sum += 1LL * a[i];   
        }
        for (int i = 0; i < n - 1; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1, 0);
        printf("%lld\n", f[1]);
    }
    return 0;
}
View Code

 

Playing with Numbers

题意:给一棵树,每个节点有一个权值还有一个模数,求每个叶子,从根到它的路上所有节点的权值的线性组合模上模数的最大值

$a_{1}$ $a_{2}$ $a_{3}$...$a_{n}$ 考虑一下它们的gcd 设为g

这n个数的线性组合本来是 $k_{1}a_{1}+k_{2}a_{2}+k_{3}a,+\ldots +k_{n}a_{n}$

全考虑成gcd后直接就变成了 k'g

所以说其实一些数的线性组合莫非就是它们的gcd的整数倍

现在就是直接看g和模数$m_{i}$

比如g = 4,模数为6 这样最大值就是2

g = 5,模数为7 最大值是6 (5 * 4 % 7 = 6)

多考虑几组就会发现答案就是$m_{i} - gcd(m_{i}, g)$

其实现在这个g,也可以进一步看成 $k_{1}g'$ g‘表示$m_{i}$和g的gcd $m_{i}$看成$k_{2}g'$

两个数相差$\Delta k$我们总有一个k可以让他们的$\Delta k$ = 1

所以就是边dfs边gcd 到叶子就统计答案就好了

#include 
#define ll long long
using namespace std;

const int maxn = 1e5 + 10;
int n;
vector<int> G[maxn];
int tol;
ll a[maxn];
ll m[maxn];
pair<int, ll> ans[maxn];

inline void init() {
    for (int i = 1; i <= n; i++) G[i].clear();
    tol = 0;  
}

inline void addedge(int u, int v) {
    G[u].push_back(v);
    G[v].push_back(u);
}

ll gcd(ll a, ll b) {
    while (b) {
        a %= b;
        swap(a, b);
    }
    return a;
}

inline void get_ans(ll g, int index) { 
    pair<int, ll> temp;
    temp.first = index;
    temp.second = m[index] - gcd(g, m[index]);
    ans[++tol] = temp;  
}

void dfs(int u, int fa, ll g) {
    bool flag = false;
    for (int v : G[u]) {
        if (v == fa) continue;
        flag = true;
        dfs(v, u, gcd(g, a[v]));
    }
    if (!flag) get_ans(g, u);
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        init();    
        for (int i = 0; i < n - 1; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            addedge(u, v);
        }        
        for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        for (int i = 1; i <= n; i++) scanf("%lld", &m[i]);
        dfs(1, 0, a[1]);
        sort(ans + 1, ans + 1 + tol);
        for (int i = 1; i <= tol; i++) {
            if (i != 1) putchar(' ');
            printf("%lld", ans[i].second);
        }
        puts("");
    }
    return 0;
}
View Code

 

Kira Loves Palindromes

题意:给一个字符串,求字符串的两个子串拼接起来后是回文串的方案数(两个子串不相交也不重复取)

区间dp

$dp_{ij}$表示第一个子串以$s_{i}$开头,第二个子串以$s_{j}$结尾符合的方案数

转移方程

$dp_{ij} = dp_{i+1,j-1} + f_{i + 1, j - 1} + b_{i + 1, j - 1} + 1$

其中$f_{ij}$表示以$s_{i}$为开头的回文串个数

$b_{ij}$表示以$s_{j}$为结尾的回文串个数

#include 
#define ll long long
using namespace std;

const int maxn = 1e3 + 10;
bool ok[maxn][maxn];
int f[maxn][maxn], b[maxn][maxn];
ll dp[maxn][maxn], ans;
char s[maxn];
int n;

void pre() {
    for (int i = n - 1; i >= 0; i--) {
        for (int j = i; j < n; j++) {
            ok[i][j] = ((s[i] == s[j]) && (j - i < 3 || ok[i+1][j-1]));
        }
    }
    for (int i = 0; i < n; i++) f[i][i] = b[i][i] = 1;
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            if (ok[i][j]) f[i][j] = f[i][j-1] + 1;
            else f[i][j] = f[i][j-1];
        }
    }
    for (int j = n - 1; j >= 0; j--) {
        for (int i = j - 1; i >= 0; i--) {
            b[i][j] = b[i+1][j];
            if (ok[i][j]) b[i][j]++;
        }
    }
}

int main() {
    scanf("%s", s);
    n = strlen(s);
    pre();
    for (int i = 0; i < n - 1; i++) {
        if (s[i] == s[i+1]) 
            dp[i][i+1] = 1, ans++;
    }
    for (int l = 3; l <= n; l++) {
        for (int i = 0; i + l - 1 < n; i++) {
            int j = i + l - 1;
            if (s[i] == s[j]) {
                dp[i][j] = f[i+1][j-1] + b[i+1][j-1] + dp[i+1][j-1] + 1;
                ans += dp[i][j];
            }
        }
    }  
    printf("%lld\n", ans);
    return 0; 
}
View Code

 

Mininum XOR over Tree

可持久化字典树...待补...

转载于:https://www.cnblogs.com/Mrzdtz220/p/10769698.html

你可能感兴趣的:(Codechef April Challenge 2019 Division 2)