2017 Chinese Multi-University Training, BeihangU Contest

多校

A:Add More Zero

\(10^n = 2^m\)
$n = lg2^m = mlg2 = m * \frac{log(n)}{log(m)} $ //以m为底n即:$\frac{log(n)}{log(m)} $

B: Balala Power!

26进制大数模拟比较
比较只有26个串,所以可以转换成字符串然后sort
前导0加点判断

struct node{
    string s; bool f2; int id;
    bool operator < (const node &a) const {
        return f2 == a.f2 ? s < a.s : f2 > a.f2;
    }
}ss[26];
int num[26][maxn];
const int mod = 1e9 + 7;
int n, cas = 0;
bool vis[26];
void run() {
    int mxl = 0;
    for(int i = 0; i < 26; ++ i)    vis[i] = 0, ss[i].s = "", ss[i].f2 = 0, ss[i].id = i;
    for(int i = 0; i < n; ++ i) {
        scanf("%s", s); int len = strlen(s);    mxl = max(mxl, len);
        if(len > 1) vis[s[0] - 'a'] = 1;
        for(int j = len - 1, sz = 0; j >= 0; -- j) num[s[j] - 'a'][sz++] ++;
    }
    for(int i = 0; i < 26; ++ i) {
        for(int j = 0; j < mxl; ++ j) {
            num[i][j + 1] += num[i][j] / 26;
            num[i][j] %= 26;
            if(num[i][mxl]) mxl ++;
        }
    }
    for(int i = 0; i < 26; ++ i)    for(int j = mxl - 1; j >= 0; -- j)  ss[i].s += 'a' + num[i][j];
    sort(ss, ss + 26);
    for(int i = 0; i < 26; ++ i)    if(!vis[ss[i].id]) {
        ss[i].f2 = 1;    break;
    }
    sort(ss, ss + 26);
    int ans = 0;
    for(int i = 0; i < 26; ++ i) {
        //cout << ss[i].s << " " << ss[i].f1 << " " << ss[i].f2 <<endl;
        int len = ss[i].s.length(), add = 0;
        for(int j = 0; j < len; ++ j)   add = (add * 26 + (ss[i].s[j] - 'a') * i) % mod;
        ans = (ans + add) % mod;
    }
    printf("Case #%lld: %lld\n", ++cas, ans % mod);

    for(int i = 0; i < 26; ++ i)    for(int j = 0; j <= mxl; ++ j)  num[i][j] = 0;
    return ;
}

C: Colorful Tree

\[\frac {n * n * (n - 1)} 2 - ans_{dfs} - \sum_{i = 1}^n C_n^{sum_i} \]

//思路:容斥,考虑对于每种颜色删去后,分成的块中的点两两之间对于这种颜色没有贡献
//可以用桶做到O(n),核心是利用了子数具有可减性。
const int maxn = 2e5 + 10;
int head[maxn], to[maxn << 1], nxt[maxn << 1], ecnt = 0, ans = 0;
inline void add(int u, int v) {
    to[++ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt;
    to[++ecnt] = u; nxt[ecnt] = head[v]; head[v] = ecnt;
}
int sum[maxn], sz[maxn], c[maxn];
inline int cn2(int n) {
    return n * (n - 1) >> 1;
}
int dfs(int now, int fa) {
    sz[now] = 1;
    for(int i = head[now]; i; i = nxt[i]) {
        int v = to[i];  if(v == fa)  continue;
        int r = sum[c[now]];//当前子节点数
        sz[now] += dfs(v, now);
        int o = sum[c[now]] - r;//下一个点的子节点数
        ans += cn2(sz[v] - o);//与下一个点之间连通块大小
        sum[c[now]] += sz[v] - o;//加上连通块中的点
    }
    sum[c[now]] ++;//自己不要忘记
    return sz[now];
}
/*
8
1 2 5 3 7 2 1 3
1 2
2 3
3 4
3 5
3 6
6 7
7 8
*/
int n, m, cas = 0;
void run() {
    for(int i = 1; i <= n; ++ i) {
        c[i] = rd();
        sum[i] = 0;
        head[i] = 0;
    }   ecnt = 0;
    for(int i = 1; i < n; ++ i) {
        int u = rd(), v = rd();
        add(u, v);
    }
    ans = 0;    dfs(1, 1);  //printf("%lld\n", ans);
    for(int i = 1; i <= n; ++ i)    ans += cn2(n - sum[i]);//sum[i]是剩下的不包含在dfs中两个颜色中的连通块,或者说是剩下的主连通块
    ans = cn2(n) * n - ans;
    //单个颜色的贡献是$\frac {n*(n-1)} 2$ 减去不含这个颜色的路径数
    //通过每个相同颜色的点将树分割为一个个连通块,那么显然连通块内路径不会经过这个颜色
    //n个颜色总贡献为$\frac {n * n * (n - 1)} 2 - ans_dfs - \sum_{i = 1}^n C_n^{sum_i} $
    printf("Case #%lld: %lld\n", ++cas, ans);
    return ;
}

F: Function

void dfs(int now, int *a) {
    if(vis[now])    return ;
    vis[now] = 1;   cnt ++;
    dfs(a[now], a);
}

void run() {
    cout << log(2) << " " << log(10) << endl;
    printf("Case #%lld: ", ++cas);
    for(int i = 0; i < m; ++ i) vis[i] = 0;
    for(int i = 0; i <= n; ++ i)    c[i] = 0;
    for(int i = 0; i < n; ++ i) a[i] = rd();
    for(int i = 0; i < m; ++ i) b[i] = rd();//b[rd()] = i; 序列等价
    for(int i = 0; i < m; ++ i) if(!vis[i])  {//求出b的所有自环大小,统计
        cnt = 0; dfs(i, b);  c[cnt] += cnt;  c[cnt] %= mod;
    }
    for(int i = n; i; --i)
        for(int j = i + i; j <= n; j += i)
            c[j] += c[i];//所有含这个因子的数都可以得到因子的贡献
    int ans = 1;
    for(int i = 0; i < n; ++ i) vis[i] = 0;
    for(int i = 0; i < n; ++ i) if(!vis[i]) {//求出a的所有自环,与b映射要求a环需要选b环大小是a的因子,计算贡献
        cnt = 0; dfs(i, a);  ans = ans * c[cnt] % mod;
    }
    printf("%lld\n", ans);
    return ;
}

H: Hints of sd0061

\(nth\_element(T^*~ a,int~k,T^ *~end) O(n)\)查询序列第\(k\)小的数
查询后将第\(k\)的数放在\(k\)的位置,比\(k\)小的数到了前部分,\(k\)大的数到了后部分
内部实现应该是类快排的分治排序算法求区间第\(k\)
通过这个性质在大区间无法排序的情况下查询少部分位置的数可通过对位置离线排序后挨个查询,这样可以慢慢减小区间
题目中满足\(b_i + b_j <= b_k\),即增长类似斐波那契速度,那么显然从后往前减小区间复杂度更优秀

void run() {
    x = A; y = B; z = C;
    for(int i = 1; i <= n; ++ i)    a[i] = rng61();
    for(int i = 1; i <= m; ++ i)    b[i] = rd(), id[i] = i;
    sort(id + 1, id + 1 + m, [](int x, int y) {return b[x] < b[y];});
    for(int i = m, r = n; i >= 1; r = b[id[i]], -- i)    {
        nth_element(a + 1, a + b[id[i]] + 1, a + r + 1);
        ans[id[i]] = a[b[id[i]] + 1];
    }
    printf("Case #%d: ", ++cas);
    for(int i = 1; i <= m; ++ i)    printf("%u%c", ans[i], " \n"[i == m]);
    return ;
}

K:

思维题,画几个样例就知道了

L: Limited Permutation

int fact[maxn], finv[maxn], inv[maxn];
inline int C(int n, int m) {
    return fact[n] * finv[m] % mod * finv[n - m] % mod;
}
void init() {
    fact[0] = fact[1] = 1;
    finv[0] = finv[1] = 1; inv[1] = 1;
    for(int i = 2; i < maxn; ++ i) {
        fact[i] = fact[i  -1] * i % mod;
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
        finv[i] = inv[i] * finv[i - 1] % mod;
    }
}

struct seg{
    int l, r, p;
    friend bool operator < (seg a,seg b){
        if(a.l!=b.l) return a.l < b.l;
        return a.r > b.r;
    }
}s[maxn];
int n, cas = 0, nowpos;
/*
首先要理解题意:当前仅当li<=L<=i<=R<=ri时P[i]=min(P[L],P[L+1],...,P[R])
因此对于P[i]一定有P[i]>P[li-1]且P[i]>P[ri+1],进一步说区间[li,ri](除了[1,n])一定被某个区间[lj,rj]包含,且j=li-1或j=ri+1
即区间j可分成[lj,j-1]和[j+1,rj]

我们把n个区间按L升序R降序进行排序(这样得到的区间LR正是前序遍历的区间,区间由大到小)。得到的第1个区间一定要是[1,n](1比任何数都小),否则不合法,输出0;
设这个区间对应的是第i个数,因此区间可再分为[1,i-1]和[i+1,n],看是否有这2个区间,如果没有则不合法,输出0...直到区间不可再分。

现在再来考虑方法数:设f(i)为区间i内的方法数,u,v分别为左右子区间,i内一共有ri-li+1个数,除去中间一个,
要从中选i-li个数放入左区间,剩下的放入右区间,因此答案为:f(i)=f(u)*f(v)*C(ri-li,i-li)
*/
void run() {
    for(int i = 1; i <= n; ++ i)    s[i].p = i, s[i].l = rd();
    for(int i = 1; i <= n; ++ i)    s[i].r = rd();
    sort(s + 1, s + 1 + n);
    //for(int i = 1; i <= n; ++ i)    dbg(i, s[i].p, s[i].l, s[i].r);
    nowpos = 1;
    function <int(int, int)> dfs;
    dfs = [&](int l, int r) {
        if(l != s[nowpos].l || r != s[nowpos].r) return 0ll;
        int mid = s[nowpos].p;
        //dbg(l, r, nowpos, mid, s[nowpos].l, s[nowpos].r);
        nowpos ++;
        int ansl = 1, ansr = 1;
        if(l < mid) ansl = dfs(l, mid - 1);
        if(r > mid) ansr = dfs(mid + 1, r);
        return ansl * ansr % mod * C(r - l, mid - l) % mod;
    };
    int ans = dfs(1, n);
    printf("Case #%lld: %lld\n", ++cas, ans);
    return ;
}

signed main() {
    init();
    while(~scanf("%lld", &n)) run();
    return 0;
}
posted @ 2021-07-13 22:14  wansheking  阅读(8)  评论(0)    收藏  举报