2017 Multi-University Training Contest - Team 1

2017多校聯合訓練1
菜雞隻能補6題。

Add More Zero

題目:求2^m - 1 > 10 ^ k,求k的最大值
水題,直接算k / lg2 即可,比賽時還傻傻的打了個表二分。。

#include <bits/stdc++.h>

#define ll long long
#define MAXN 100005

using namespace std;

ll m;
double t;

bool check(ll n) {
    return m > n / t;
}

ll binary_search(ll l, ll r) {
    ll mid = l + r >> 1;
    ll ans = mid;
    while (l <= r) {
        mid = l + r >> 1;
        if (check(mid)) {
            ans = mid;
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }
    return ans;
}

ll ans[MAXN];

int main() {
    int cas = 1;
    t = log10(2);
    for (ll i = 0; i <= 100000; i++) {
        m = i; 
        ans[i] = binary_search(0, 100000);
    }
    while (~scanf("%lld", &m)) {
        printf("Case #%d: %lld\n", cas++, ans[m]);
    }
}

Balala Power!

個人感覺挺噁心的一道模擬貪心題。

題目意思是。給n條只由小寫字母組成的字符串,你可以將a-z逐一表示成0-25,現在問將每一串表示成26進制數後,相加求10進制的最大值。

思路:將所有串按照右部爲尾端來構成一個序列,每一個串爲一行,對每一列從右邊開始加就有26^0*xxx + 26^1*yyy…
這裏只需要將整個字符串倒置即可。。
按照每一列字符出現的次數排序,如果相同則比較下一列。
然後貪心選取最先出現的小寫字符,將他們賦值。
最後要注意到不能有前導0,則讀入的時候標記一下不能爲前導0的字符。
所有數賦值後,若最後一個數爲0且爲某一串開頭,則逐步往前移動至可以爲0開頭的字符。

這裏要注意。統計每一列的字符的個數的時候,要記得進位!!!!
跟隊友wa死了一下午這裏。。

#include <bits/stdc++.h>

#define MAXN 100025
#define ll long long
#define MOD 1000000007

using namespace std;

struct node {
    ll idx, val, let; 
    node(){}
    node(ll _idx, ll _val, ll _let) {
        idx = _idx;
        val = _val;
        let = _let;
    }
} lets[30];

ll tot[MAXN][30];
ll rec[MAXN];
ll val[30];
bool head[30];
ll lmax;

int cmp(node a, node b) {
    if (a.val != b.val) {
        return a.val > b.val;
    }
    for (ll i = a.idx - 1; i >= 0; i--) {
        if (tot[i][a.let] != tot[i][b.let]) {
            return tot[i][a.let] > tot[i][b.let];
        }
    }
    return 0;
}

int cmp2(node a, node b) {
    return a.val < b.val;
}

void init() {
    memset(tot, 0, sizeof(tot));
    memset(head, false, sizeof(head));
    memset(val, -1, sizeof(val));
    rec[0] = 1;
    for (ll i = 1; i < MAXN; i++) {
        rec[i] = (rec[i - 1] * 26) % MOD;
    }
}

ll calc() {
    ll ans = 0;
    for (ll i = 0; i < lmax; i++) {
        for (ll j = 0; j < 26; j++) {
            if (val[j] != -1 && tot[i][j]) {
                ans = (ans + (((val[j] * rec[i]) % MOD) * tot[i][j]) % MOD) % MOD;
            }
        }
    }
    return ans;
}

int main() {
    ll n, cas = 1;
    char ch[MAXN];
    while (~scanf("%lld", &n)) {
        init();
        lmax = 0;
        ll maxt = 25;
        for (ll i = 0; i < n; i++) {
            scanf("%s", ch);
            ll len = strlen(ch);
            if (len > 1) {
                head[ch[0] - 'a'] = true;
            }
            lmax = max(lmax, len);
            for (ll j = 0; j < len; j++) {
                tot[j][ch[len - 1 - j] - 'a']++; 
                if (tot[j][ch[len - 1 - j] - 'a'] == 26) {
                    tot[j][ch[len - 1 - j] - 'a'] = 0;
                    tot[j + 1][ch[len - 1 - j] - 'a']++;
                    if (j == len - 1) {
                        lmax = max(lmax, len + 1);
                    }
                }
            }
        }
        for (ll i = lmax - 1; i >= 0; i--) {
            for (ll j = 0; j < 26; j++) {
                lets[j] = node(i, tot[i][j], j);
            }
            sort(lets, lets + 26, cmp);
            for (ll j = 0; j < 26; j++) {
                if (val[lets[j].let] == -1 && tot[i][lets[j].let]) {
                    val[lets[j].let] = maxt--;
                }
            }
        }
        if (maxt == -1) {
            for (ll i = 0; i < 26; i++) {
                lets[i] = node(0, val[i], i);
            }
            sort(lets, lets + 26, cmp2);
            if (head[lets[0].let]) {
                ll tmp = 1;
                while (head[lets[tmp].let] && tmp < 26) {
                    tmp++;
                }
                if (tmp != 26) {
                    for (ll i = 0; i < tmp; i++) {
                        val[lets[i].let] = val[lets[i + 1].let];
                    }
                    val[lets[tmp].let] = 0;
                }
            }
        }
        printf("Case #%lld: %lld\n", cas++, calc());
    }
}

Colorful Tree

題意:給一棵樹,每兩點之間的距離爲:他們路徑上所有點不同顏色的個數。現在求對於整個圖的n * (n - 1) / 2條路徑,他們總距離爲多少。

思路:
該題可以將模型進行轉換,求總圖每兩點之間的距離之和,可以看成,對於每一個顏色,經過它的不同路徑有多少條,求所有顏色分別經過他們的不同路徑之和。
還可以轉換成,所有路徑*總顏色個數,除去不經過某種顏色的路徑之和,即爲答案。
orz摩拜tls大佬想出這種題。

類似樹形dp,對於每一個顏色的節點,維護該點顏色時,他的子節點除去了相同顏色子樹的個數。
最後要注意到,最頂部的那個節點的其他顏色是沒有dp到的,最後再組合維護一下即可。

#include <bits/stdc++.h>

#define MAXN 200005
#define ll long long

using namespace std;

ll col[MAXN];
ll size[MAXN]; //記錄子節點數
ll sum[MAXN]; //記錄截斷值,即爲該點到所有不同顏色的節點,到相同顏色的節點時結束。 
vector<ll> g[MAXN];
bool vis[MAXN];
ll mark[MAXN];
ll ans;

void addEdge(ll u, ll v) {
    g[v].push_back(u);
}

void init() {
    ans = 0;
    memset(mark, 0, sizeof(mark));
    memset(vis, false, sizeof(vis));
    memset(size, 0, sizeof(size));
    memset(sum, 0, sizeof(sum));
    for (ll i = 0; i < MAXN; i++) {
        g[i].clear();
    }
}

void dfs(ll u) {
    size[u] = 1;
    vis[u] = true;
    ll len = g[u].size();
    ll all = 0;
    for (ll i = 0; i < len; i++) {
        ll to = g[u][i];
        ll s = sum[col[u]];
        if (vis[to]) {
            continue;
        }
        dfs(to);
        size[u] += size[to];
        ll step = sum[col[u]] - s;  //截斷後的數量 
        all += step;
        ans += (size[to] - step) * (size[to] - step - 1) / 2;
    }
    sum[col[u]] += size[u] - all;
}

int main() {
    ll n, u, v, cas = 1;
    while (~scanf("%lld", &n)) {
        init();
        ll tt = 0;
        for (ll i = 1; i <= n; i++) {
            scanf("%lld", &col[i]);
            tt += mark[col[i]] ^ 1;
            mark[col[i]] = 1;
        }
        for (ll i = 0; i < n - 1; i++) {
            scanf("%lld %lld", &u, &v);
            addEdge(u, v);
            addEdge(v, u);
        }
        dfs(1);
        ll lstans = n * (n - 1) * tt / 2;
        for (ll i = 1; i <= n; i++) {
            if (tt != col[1] && mark[i]) {
                ans += (n - sum[i]) * (n - sum[i] - 1) / 2;
            }
        } 
        printf("Case #%lld: %lld\n", cas++, lstans - ans);
    }
}

Function

題意:對於f(i)=bf(ai)一個函數,有a和b序列,現在問你可以構成f(x)的情況有多少種
不知從何找到的規律,將a序列和b序列根據下標和值打環,若a的某個環上的點可以整除b的某個環上的點。則肯定滿足。
打環統計一下數就好了。。暴力最大情況O(n * m) 卡過了。。

#include <bits/stdc++.h>

#define MAXN 100005
#define ll long long
#define MOD 1000000007

using namespace std;

bool mark[MAXN];
ll a[MAXN], b[MAXN];
ll alen, blen, t;
vector<ll> res[2];

void init() {
    alen = blen = 0;
    for (ll i = 0; i < 2; i++) {
        res[i].clear();
    }
}

void dfs(ll u, ll *num) {
    if (mark[u]) {
        return ;
    }
    mark[u] = true;
    t++;
    dfs(num[u], num);
}

int main() {
    ll n, m, cas = 1;
    while (~scanf("%lld %lld", &n, &m)) {
        init();
        for (ll i = 0; i < n; i++) {
            scanf("%lld", &a[i]);
        }
        for (ll i = 0; i < m; i++) {
            scanf("%lld", &b[i]);
        }
        memset(mark, false, sizeof(mark));
        for (ll i = 0; i < n; i++) {
            if (!mark[i]) {
                t = 0;
                dfs(i, a);
                res[0].push_back(t);
            }
        }
        memset(mark, false, sizeof(mark));
        for (ll i = 0; i < m; i++) {
            if (!mark[i]) {
                t = 0;
                dfs(i, b);
                res[1].push_back(t);
            }
        }
        alen = res[0].size(), blen = res[1].size();
        ll ans = 1;
        for (ll i = 0; i < alen; i++) {
            ll tt = 0;
            for (ll j = 0; j < blen; j++) {
                if (res[0][i] % res[1][j] == 0) {
                    tt += res[1][j];
                    tt %= MOD;
                }
            }
            ans = (ans * tt) % MOD;
        }
        printf("Case #%lld: %lld\n", cas++, ans);
    }
}

Hints of sd0061

題目:給一個遞推函數構成的ai序列,m次詢問,問第bi小的數的值是多少。(從0開始)
思路:直接排序是不行的,將詢問的方式排個序,用nth_element每次縮小區間找第n大的數即可。

#include <bits/stdc++.h>

#define ull unsigned int
#define MAXN 10000005

using namespace std;

ull x, y, z;
ull a[MAXN];
ull ans[105];
int b[105], pos[105];

ull rng61() {
    ull t;
    x ^= x << 16;
    x ^= x >> 5;
    x ^= x << 1;
    t = x;
    x = y;
    y = z;
    z = t ^ x ^ y;
    return z;
}

int cmp(int aa, int bb) {
    return b[aa] < b[bb];
}

int main() {
    ull A, B, C;
    int n, m;
    int cas = 1;
    while (~scanf("%d %d %u %u %u", &n, &m, &A, &B, &C)) {
        x = A, y = B, z = C;
        for (int i = 0; i < m; i++) {
            scanf("%d", &b[i]);
            pos[i] = i;
        }
        sort(pos, pos + m, cmp);
        for (int i = 0; i < n; i++) {
            a[i] = rng61();
        }
        pos[m] = m;
        b[pos[m]] = n;
        for (int i = m - 1; i >= 0; i--) {
            nth_element(a, a + b[pos[i]], a + b[pos[i + 1]]);
            ans[pos[i]] = a[b[pos[i]]];
        }
        printf("Case #%d:", cas++);
        for (int i = 0; i < m; i++) {
            printf(" %u", ans[i]);
        }
        puts("");
    }
} 

KazaQ’s Socks

題目:有一個人穿襪子,每次選最小號碼的襪子,每n-1天會把襪子拿去洗一次,問第k天穿了哪個號嗎的襪子。
水題,找到循環節,前n個數爲1-n,後面每2 * (n - 1)一個循環節。

#include <bits/stdc++.h>

#define ll long long

using namespace std;

int main() {
    ll n, k, cas = 1;
    while (~scanf("%lld %lld", &n, &k)) {
        printf("Case #%lld: ", cas++);
        if (k <= n) {
            printf("%lld\n", k);
        } else {
            ll p = (k - n) % (2 * (n - 1));
            if (p == 0) {
                p = 2 * (n - 1);
            } 
            if (p <= n - 1) {
                printf("%lld\n", p);
            } else {
                p -= n - 1;
                if (p == n - 1) {
                    printf("%lld\n", n);
                } else {
                    printf("%lld\n", p);
                }
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章