2017 Multi-University Training Contest 1 (9/12)

1001 - Add More Zero

题:在 [0, 2m1] 求最大的 k ,使得满足 010k2m1

限制: 1m105

解: 10k2m1 等价于 10k2m ,求 k=log102m

#include<bits/stdc++.h>
using namespace std;
int main()
{
    for(int ica=1, m;scanf("%d", &m)!=EOF;ica++)
        printf("Case #%d: %d\n", ica, (int)(log10(2.0)*m));
}

1002 - Balala Power!

Problem

n 个字符串 si 中的每个字符 a-z 对应映射到 0-25 。将结果字符串看作 26 进制的整数。求任意映射方式使得 n 个字符串的和最大。其中每种字符只能映射到同一个数,每个数最多对应一种字符。同时每个结果字符串(除原字符串长为 1 外)均不能出现前导 0 。字符串和对 109+7 取模。

Limit

1n100000

1si100000,si106

Idea

  1. 统计每种字符对字符串和的贡献,在每个末位的字符的贡献为 260×?? ,在次末位的字符的贡献为 261×?? ,依次类推。其中 ?? 表示该字符映射的数。记录到 cnt[26][MAX_LENGTH] 的数组种保存。
  2. 注意二十六进制数进位的可能,满 26 进 1 对 cnt 数组进行处理
  3. 针对每个字符的贡献大小进行排序,贪心方式给每个字符的 ?? 赋值,贡献最大的 25 ,其次 24 …
  4. 当 26 个字符都出现的时候,注意特殊处理贡献最小的数在非单字符字符串首的可能,需找到对字符串和贡献尽可能小且可为 0 的字符,并将其移至序列最后
  5. 线性处理字符串和。

Code

#include<bits/stdc++.h>
using namespace std;
const long long mod = 1e9 + 7;
const int N = 100000 + 10;
string s[N];
bool vis[33], ispre[33];
int n, num[33], maxlen;
long long MI[N];
int cnt[33][N], len[N];
bool cmp(int x, int y) {
    for(int i=maxlen-1;i>=0;i--) {
        if(cnt[x][i] > cnt[y][i])    return true;
        else if(cnt[x][i] < cnt[y][i])    return false;
    }
    return x > y;
}
void Qsort(int low, int high)
{
    if(low >= high)    return;
    int first = low;
    int last = high;
    int key = num[first];
    while(first < last) 
    {
        while(first < last && cmp(num[last], key))
            --last;
        num[first] = num[last];
        while(first < last && cmp(key, num[first]))
            ++first;
        num[last] = num[first];
    }
    num[first] = key;
    Qsort(low, first-1);
    Qsort(first+1, high);
}
int main()
{
    MI[0] = 1;
    for(int i=1;i<=100000;i++)
        (MI[i] = MI[i-1] * 26) %= mod;
    for(int ica=1;scanf("%d", &n)!=EOF;ica++)
    {
        maxlen = 0;
        memset(vis, 0, sizeof(vis));
        memset(cnt, 0, sizeof(cnt));
        memset(ispre, 0, sizeof(ispre));
        for(int i=1;i<=n;i++)
        {
            cin>>s[i];
            len[i] = s[i].size();
            maxlen = max(maxlen, len[i]);
            if(len[i] != 1)
                ispre[s[i][0]-'a'] = true;
            for(int pos=0;pos<len[i];pos++)
            {
                cnt[(int)(s[i][len[i]-pos-1]-'a')][pos]++;
                vis[(int)(s[i][len[i]-pos-1]-'a')] = true;
            }
        }

        maxlen += 7;
        for(int i=0;i<26;i++)
        for(int pos=0;pos<maxlen;pos++)
        {
            if(cnt[i][pos] >= 26) {
                cnt[i][pos+1] += cnt[i][pos] / 26;
                cnt[i][pos] %= 26;
            }
        }

        int idx = 0;
        for(int i=0;i<26;i++)
            if(vis[i])    num[idx++] = i;

        Qsort(0, idx-1);
        for(int i=0;i<idx/2;i++)
            swap(num[i], num[idx-i-1]);

        if(idx == 26 && ispre[ num[25] ]) {
            int p = 25;
            for(;p>=0;p--)
                if(ispre[ num[p] ] == 0)    break;
            for(p;p<25;p++)
                swap(num[p], num[p+1]);
        }

        long long ans = 0;
        for(int i=0;i<idx;i++)
        for(int j=0;j<maxlen;j++)
            (ans += MI[j] * cnt[ num[i] ][j] % mod * (25-i) % mod) %= mod;
        printf("Case #%d: %lld\n", ica, ans);
    }
}

1003 - Colorful Tree

Problem

n 个点 n-1 条边的树,每个点都有其颜色 ci 。记任意两个点 (i, j) 的 value 为两点间最短路径上不同颜色数。求所有 n(n1)2 种两点组合的 value 和。

Limit

2n200000

1cin

Idea

统计 value 和可以认为是求每种颜色对结果的贡献。

对于颜色 c 而言,若其在路径 (i, j) 出现,则其在这条路径对结果的贡献为 1 (无论其在这条路径中出现多少次)。

若反向考虑其在多少路径中没有出现,则可以将图看作是被所有颜色 c 所划分后的各连通块。若连通块的点数为 m ,则在该连通块内,任意两点间路径必不存在 c ,故减掉其贡献 m(m1)2

快速将树按照颜色 c 分块可以预处理出每个点的 dfs 序及以其为根节点的树的大小。遍历所有颜色为 c 的点的编号判断其在当前递归的子树内或需返回上一层子树进行处理。

Code

#include<bits/stdc++.h>
using namespace std;
const int E = 200000 + 10;
const int N = 200000 + 10;
struct Node {
    int nxt, to;
} e[E*2];
int n, c[N], head[N], cnt, dfn[N], dfnidx, siz[N], idx;
long long ans;
vector<int> v[N];
void addedge(int x, int y) {
    e[++cnt].nxt = head[x];
    e[cnt].to = y;
    head[x] = cnt;
}
bool cmp(int x, int y) {
    return dfn[x] < dfn[y];
}
void init() {
    memset(head, 0, sizeof(head));
    memset(dfn, 0, sizeof(dfn));
    memset(siz, 0, sizeof(siz));
    cnt = 0;
    dfnidx = 0;
}
int dfs(int rt) {
    dfn[rt] = ++dfnidx;
    for(int i=head[rt];i;i=e[i].nxt)
    {
        if(dfn[ e[i].to ])  continue;
        siz[rt] += dfs(e[i].to);
    }
    return siz[rt]+1;
}
void solve(int rt, int num, int color) {
    idx++;
    for(int id;idx<v[color].size();idx++)
    {
        id = v[color][idx];
        if(dfn[id] - dfn[rt] > siz[rt]) {
            idx--;  break;
        }
        num -= (siz[id]+1);
        for(int i=head[id];i;i=e[i].nxt)
        {
            if(dfn[e[i].to] < dfn[id])  continue;
            solve(e[i].to, siz[e[i].to] + 1, color);
        }
    }
    ans -= (long long) num * (num-1) / 2;
}
int main()
{
    for(int ica=1;scanf("%d", &n)!=EOF;ica++)
    {
        init();
        for(int i=1;i<=n;i++) {
            scanf("%d", &c[i]);
            v[ c[i] ].push_back(i);
        }
        for(int i=1, x, y;i<n;i++)
        {
            scanf("%d %d", &x, &y);
            addedge(x, y);
            addedge(y, x);
        }
        dfs(1);
        addedge(0, 1);
        siz[0] = n;
        ans = 0;
        for(int i=1;i<=n;i++)
        {
            if(v[i].size() == 0)    continue;
            sort(v[i].begin(), v[i].end(), cmp);
            ans += (long long) n * (n-1) / 2;
            idx = -1;
            solve(0, n, i);
            v[i].clear();
        }
        printf("Case #%d: %lld\n", ica, ans);
    }
}

1006 - Function

Problem

a 数组有 0~n-1 的 n 个数,b 数组有 0~m-1 的 m 个数。

求多少种不同的映射关系,使得 f(i)=bf(ai) i[0, n) 恒成立。方案数对 109+7 取模。

Limit

1n100000

1m100000

n106

m106

Idea

统计 a, b 数组的循环节个数。方案数为 a 数组每种循环节长度 L 的个数 × L 的每个因子在 b 数组中对应循环节个数和。

Code

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int N = 100000 + 10;
const int M = 100000 + 10;
int a[N], b[M], vis[N], cnt, c[N];
void solve(int arr[], int x) {
    if(vis[x])  return;
    vis[x] = 1;
    cnt++;
    solve(arr, arr[x]);
}
int main() 
{
    for(int ica=1, n, m;scanf("%d %d", &n, &m)!=EOF;ica++)
    {
        memset(vis, 0, sizeof(vis));
        memset(c, 0, sizeof(c));
        for(int i=0;i<n;i++)
            scanf("%d", &a[i]);
        for(int i=0;i<m;i++)
            scanf("%d", &b[i]);
        for(int i=0;i<m;i++) {
            if(vis[i])  continue;
            cnt = 0;
            solve(b, i);
            c[cnt]++;
        }
        long long ans = 1;
        memset(vis, 0, sizeof(vis));
        for(int i=0;i<n;i++)
        {
            if(vis[i])  continue;
            cnt = 0;
            solve(a, i);
            long long tmp = 0;
            for(int j=1;j*j <= cnt;j++) {
                if(cnt % j == 0) {
                    tmp += c[j] * j;
                    if(cnt / j != j)
                        tmp += c[cnt/j] * (cnt/j);
                }
            }
            if(tmp == 0)    ans = 0;
            else    (ans *= tmp) %= mod;
        }
        printf("Case #%d: %lld\n", ica, ans);
    }
}

1007 - Gear Up

Problem

给定若干半径为 2λ(0λ30) 的齿轮,齿轮有两种连接方式:

  • 1 x y 表示 x 与 y 线速度相同
  • 2 x y 表示 x 与 y 角速度相同

有 Q 组询问,每个询问为下列中的一种:

  • 1 x y 表示将齿轮 x 的半径改为 y y2λ
  • 2 x y 表示求给定齿轮 x 角速度为 y ,当前旋转最快齿轮的角速度?

Idea

此图构成森林,故添加一个超级点 0 号将其看作时一个有根树。对于 x 与 y 共边的情况, ωxrx=ωyry 。两边同取对数, logωx=logωy+logrylogrx 。若 x 与 y 共轴,则角速度相同。

故利用并查集找到每组共轴点的集合。

DFS 根据齿轮间的关系建图,优先深搜当前结点 rt 共轴点的后续共边点。并维护每个点的 dfs 序 dfn[i],每个点的最大子树点 dfs 序 rgt[i]。同时判断当前点 rt 的父节点是否与 rt 共边,标记。当然也需要记录每个点所属的树的根节点(与 0 直接相连点编号)。

构造线段树,对于 1 类操作,若齿轮 x 与其父节点共边,则修改区间 [dfn[i], rgt[i]] 的角速度。否则修改区间 [dfn[i]+1, rgt[i]]

对于 2 类操作,直接查询其所在树的最大值及 x 点的相对角速度,简单计算即可。

Code

#include<bits/stdc++.h>
using namespace std;
const int N = 100000 + 10;
const int E = 100000 + 10;
const int inf = 1e9+7;
int n, m, q, a, x, y, r[N], logr[N], fa[N];
struct Edge {
    int nxt, to;
} e[E * 2];
int head[N], cnt;
void addedge(int u, int v) {
    e[++cnt].nxt = head[u];
    e[cnt].to = v;
    head[u] = cnt;
}
int find(int x) {   return fa[x] = x == fa[x] ? x : find(fa[x]);    }
void Union(int x, int y) {  
    int fx = find(x),   fy = find(y);
    if(fx == fy)    return;
    if(fx > fy) swap(fx, fy);
    fa[fy] = fx;
}
void initFa() { for(int i=1;i<=n;i++)   fa[i] = i;  }

// DFS part
vector<int> uGear[N];
int dfn[N], idfn, val[N], rgt[N], root[N], siz[N];
bool typeOne[N];
void dfs(int rt, int fa, int suRoot) {
    root[rt] = suRoot;
    typeOne[rt] = 1;
    dfn[rt] = ++idfn;
    val[idfn] = val[ dfn[fa] ] + (logr[fa] - logr[rt]);
    for(int i=0, tmp = find(rt);i<uGear[tmp].size();i++) {
        if(uGear[tmp][i] == rt || dfn[ uGear[tmp][i] ]) continue;
        dfn[ uGear[tmp][i] ] = ++idfn;
        root[ uGear[tmp][i] ] = suRoot;
        val[idfn] = val[ dfn[fa] ] + (logr[fa] - logr[rt]);
        for(int j=head[uGear[tmp][i]];j;j=e[j].nxt) {
            if(dfn[ e[j].to ])  continue;
            dfs(e[j].to, uGear[tmp][i], suRoot);
        }
        rgt[ uGear[tmp][i] ] = idfn;
    }
    rgt[rt] = idfn;
    for(int j=head[rt];j;j=e[j].nxt) {
        if(dfn[ e[j].to ])  continue;
        dfs(e[j].to, rt, suRoot);
    }
}

// Segment Tree 
#define lson l , m , rt << 1  
#define rson m + 1 , r , rt << 1 | 1 
const int MAXN = 200000 + 10;
int add[MAXN<<2], mx[MAXN<<2], M[MAXN<<2];
void PushUp(int rt) {   mx[rt] = max(mx[rt<<1], mx[rt<<1|1]);   }
void PushDown(int rt, int m) {
    if(add[rt]) {
        add[rt<<1] += add[rt];
        add[rt<<1|1] += add[rt];
        mx[rt<<1] += add[rt];
        mx[rt<<1|1] += add[rt];
        add[rt] = 0;
    }
}
void build(int l, int r, int rt) {
    add[rt] = 0;
    if(l == r) {
        mx[rt] = val[l];
        return;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
void update(int L, int R, int w, int l, int r, int rt) {
    if(L>R) return;
    if(L <= l && r <= R) {
        add[rt] += w;
        mx[rt] += w;
        return;
    }
    PushDown(rt, r-l+1);
    int m = (l+r) >> 1;
    if(L <= m)  update(L, R, w, lson);
    if(m < R)   update(L, R, w, rson);
    PushUp(rt);
}
int query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R)    return mx[rt];
    PushDown(rt, r-l+1);
    int m = (l+r) >> 1;
    int ret = -inf;
    if(L <= m)  ret = max(ret, query(L, R, lson));
    if(m < R)   ret = max(ret, query(L, R, rson));
    return ret;
}
int main()
{
    for(int ica=1;scanf("%d %d %d", &n, &m, &q)!=EOF;ica++)
    {
        initFa();
        memset(head, 0, sizeof(head));  cnt = 0;
        for(int i=1;i<=n;i++)
            scanf("%d", &r[i]), logr[i] = log2(r[i]);
        for(int i=1;i<=m;i++) {
            scanf("%d %d %d", &a, &x, &y);
            if(a == 1)  addedge(x, y),  addedge(y, x);
            else    Union(x, y);    
        } 

        for(int i=1;i<=n;i++)   uGear[i].clear();
        for(int i=1;i<=n;i++)   uGear[find(i)].push_back(i);

        // DFS build tree
        idfn = 0;
        memset(root, 0, sizeof(root));
        memset(dfn, 0, sizeof(dfn));
        memset(typeOne, 0, sizeof(typeOne));
        for(int i=1;i<=n;i++) {
            if(dfn[i])  continue;
            dfs(i, 0, i);   
            siz[i] = idfn;
        }

        build(1, n, 1);
        printf("Case #%d:\n", ica);
        for(int i=1;i<=q;i++)
        {
            scanf("%d %d %d", &a, &x, &y);
            if(a == 1) {
                if(typeOne[x]) {
                    update(dfn[x], rgt[x], logr[x] - log2(y), 1, n, 1);
                } else {
                    update(dfn[x]+1, rgt[x], log2(y)-logr[x], 1, n, 1);
                }   
                logr[x] = log2(y);
            } else {
                int curv = query(dfn[x], dfn[x], 1, n, 1);
                int maxv = query(dfn[root[x]], siz[root[x]], 1, n, 1);
                printf("%.3lf\n", (log2(y)+maxv-curv+0.0) * log(2.0));  
            }
        }
    }   
}

1008 - Hints of sd0061

Problem

根据 rng61() 函数和初始种子 A, B, C 生成 n 个随机数。m 个询问 bi ,求 n 个随机数中第 bi+1 小的数。

其中保证对于任意的 bibj bi<bk bj<bk ,一定满足 bi+bjbk

Limit

1n107

1m100

0bi<n

Idea

对于求序列 k 小数,可以直接使用 STL 的 nth_element 或者实现基于快排的求第 k 小数算法。复杂度近似 O(n)

Complexity (nth_element)

On average, linear in the distance between first and last: Compares elements, and possibly swaps (or moves) them, until the elements are properly rearranged

对于每组询问,如果优先对 bi 进行排序,则前次 nth_element 可对后次的操作产生影响。

例如优先查找 [1, 200] 中第 100 小数。

则此时的 [1, 99] 均比数组中第 100 个数小,[101, 200] 均大于第 100 个数。

故若此时查询第 50 小数只需在 [1, 100] 区间内查找即可。

对于 m 组询问,由于 bi+bj≤bk is satisfied if bi≠bj, bi<bk and bj<bk. 故可以考虑要使得 bi 的在 [1, n] 的范围内数量最多,其一定要是类似斐波那契数列的形式,而斐波那契数的相邻两数间的公比均近似 1.618 。

因此,整体复杂度可认为是 O(in1.618i)O(n)

Code

#include<bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10;
const int M = 100 + 10;
int n, m;
struct Node {
    int id, b;
    unsigned ans;
} b[M];
bool cmp_b(Node a, Node b) {
    return a.b < b.b;
}
bool cmp_id(Node a, Node b) {
    return a.id < b.id;
}
unsigned x, y, z, a[N], ans[M];
unsigned rng61() {
    unsigned t;
    x ^= x << 16;
    x ^= x >> 5;
    x ^= x << 1;
    t = x;
    x = y;
    y = z;
    z = t ^ x ^ y;
    return z;
}
int partition(unsigned arr[], int left, int right)
{
    if(left > right)    return -1;
    int pivot = arr[left], l = left, r = right;
    while(l<r) 
    {
        while(l < r && arr[r] > pivot)    r--;
        if(l < r)    arr[l++] = arr[r];
        while(l < r && arr[l] < pivot)    l++;
        if(l < r)    arr[r--] = arr[l];
    }
    arr[l] = pivot;
    return l;
}
unsigned findKth(unsigned arr[], int left, int right, int k)
{
    if(!(left <= right && k >= 1 && k <= right-left+1)) {
        vector<long long> v;
        for(int i=1;1;i++)    v.push_back(i);
    }
    if(left == right)    return arr[left];
    int pos = partition(arr, left, right);
    if(pos-left+1 == k)    return arr[pos];
    else if(pos-left+1 > k)    return findKth(arr, left, pos-1, k);
    else    return findKth(arr, pos+1, right, k-(pos-left+1)); 
}
int main()
{
    for(int ica=1;scanf("%d %d %u %u %u", &n, &m, &x, &y, &z)!=EOF;ica++)
    {
        for(int i=1;i<=n;i++)
            a[i] = rng61();
        for(int i=0;i<m;i++) {
            scanf("%d", &b[i].b);
            b[i].id = i;    b[i].b++;
        }
        sort(b, b+m, cmp_b);

        b[m].b = n+1;
        for(int i=m-1;i>=0;i--)
        {
            if(i!=m-1 && b[i].b == b[i+1].b)    b[i].ans = b[i+1].ans;
            else {
                nth_element(a+1, a+b[i].b, a+b[i+1].b);
                b[i].ans = a[ b[i].b ];
            }
        }
        sort(b, b+m, cmp_id);

        printf("Case #%d:", ica);
        for(int i=0;i<m;i++)
            printf(" %u", b[i].ans);
        printf("\n");
    }
}

1009 - I Curse Myself

Problem

给定仙人掌图,定义 V(k) 表示第 k 小生成树的边权和。求 KkV(k)

Idea

对于仙人掌图,找到每个环并删除环上任意一条边,就可以得到一个生成树。

故首先 DFS 统计环数及对应环上的边的权值信息,用向量组 nval 或集合组的形式保持。

根据向量组中的信息两两进行合并,保持权值和最大的 K 个数(可能初始合并时不足 K 种权值和)。

线性遍历所有 K 个数,总边权和 sumval - 当前第 i 个数的大小即第 i 小生成树。

Code

#include<bits/stdc++.h>
using namespace std;
const int E = 2000 + 10;
const int N = 1000 + 10;
const int MAX_K = 100000 + 10;
int n, m, K, head[N], cnt, vis[N], ival, stk[N], path[N];
unsigned tval[2][MAX_K];
vector<long long> nval[N];
struct P {    int fir, sec;    } p, q;
bool operator<(P a, P b) {
    return a.fir < b.fir;
}
bool cmp(int x, int y) {    return x > y;    }
struct Node {
    int nxt, to, val, mrk;
} e[E * 2];
void addedge(int u, int v, int val) {
    e[++cnt].nxt = head[u];
    e[cnt].to = v;
    e[cnt].val = val;
    e[cnt].mrk = 0;
    head[u] = cnt;
}
void dfs(int rt, int fa, int dep) {
    if(vis[rt]) {
        ival++;
        for(int i=vis[rt];i<dep;i++) {
            nval[ival].push_back(e[ path[i] ].val);
            e[ path[i] ].mrk = e[ path[i]^1 ].mrk = 1;
        }
        sort(nval[ival].begin(), nval[ival].end(), cmp);
        return;
    }
    vis[rt] = dep;
    for(int i=head[rt];i;i=e[i].nxt) {
        if(e[i].to == fa || e[i].mrk)    continue;
        path[ dep ] = i;
        dfs(e[i].to, rt, dep+1);
    }
    vis[rt] = 0;
}
void init() {
    for(int i=1;i<=ival;i++)
        nval[i].clear();
    memset(tval, 0, sizeof(tval));
    memset(head, 0, sizeof(head));
    cnt = 1;
    ival = 0;
}
int main()
{
    for(int ica=1;scanf("%d %d", &n, &m)!=EOF;ica++)
    {
        init();
        unsigned sumval = 0;
        for(int i=1, x, y, z;i<=m;i++)
        {
            scanf("%d %d %d", &x, &y, &z);
            sumval += z;
            addedge(x, y, z);
            addedge(y, x, z);
        }
        scanf("%d", &K);
        if(n == m+1)    {    printf("Case #%d: %u\n", ica, sumval);    continue;    }
        dfs(1, 0, 1);
        priority_queue<P> que;
        int it = 0;
        while(!que.empty())    que.pop();
        for(int j=0;j<nval[1].size() && j<K;j++) {
            tval[it][j] = nval[1][j];
        }
        for(int i=2;i<=ival;i++)
        {
            while(!que.empty())    que.pop();
            int tidx = 0;
            for(int j=0;j<nval[i].size();j++) {
                p.fir = tval[it][0] + nval[i][j];    p.sec = 0;
                que.push(p);
            }

            for(int j=0;j<K && !que.empty();j++)
            {
                p = que.top();
                que.pop();
                tval[1-it][tidx++] = p.fir;
                if(tval[it][p.sec+1] == 0)    continue;
                q.fir = p.fir - tval[it][p.sec] + tval[it][p.sec+1];    q.sec = p.sec+1;
                que.push(q);
            }
            it = 1-it;
        }
        unsigned int ans = 0; 
        for(unsigned j=0;tval[it][j];j++)
            ans += (j+1) * (sumval - tval[it][j]);
        printf("Case #%d: %u\n", ica, ans);
    }
}

1011 - KazaQ’s Socks

Problem

KazaQ wears socks everyday.

At the beginning, he has n pairs of socks numbered from 1 to n in his closets.

Every morning, he puts on a pair of socks which has the smallest number in the closets.

Every evening, he puts this pair of socks in the basket. If there are n−1 pairs of socks in the basket now, lazy KazaQ has to wash them. These socks will be put in the closets again in tomorrow evening.

KazaQ would like to know which pair of socks he should wear on the k-th day.

Limit

2n109

1k1018

Idea

1,2,,nn numbers, 1,2,,n1n1 numbers, 1,2,,n2,nn1 numbers, 1,2,,n1n1 numbers, 1,2,,n2,nn1 numbers,

  • kn , 直接输出 k 。
  • k>n , 求 k÷n 的商 div 和余数 mod .
    • mod=0 , 输出 n-mod%2
    • mod0 , 输出 mod

Code

#include<bits/stdc++.h>
using namespace std;
long long n, k;
int main()
{
    for(int ica=1;scanf("%lld %lld", &n, &k)!=EOF;ica++)
    {
        if(k <= n)  printf("Case #%d: %lld\n", ica, k);
        else {
            k -= n;
            long long div = k / (n-1);
            long long mod = k % (n-1);
            if(mod == 0)    printf("Case #%d: %lld\n", ica, n-div%2);
            else    printf("Case #%d: %lld\n", ica, mod);
        }
    }
}

1012 - Limited Permutation

Problem

As to a permutation p1,p2,,pn from 1 to n, it is uncomplicated for each 1≤i≤n to calculate (li,ri) meeting the condition that min(pL,pL+1,⋯,pR)=pi if and only if li≤L≤i≤R≤ri for each 1≤L≤R≤n.

Given the positive integers n, (li,ri) (1≤i≤n), you are asked to calculate the number of possible permutations p1,p2,,pn from 1 to n, meeting the above condition.

The answer may be very large, so you only need to give the value of answer modulo 109+7 .

Limit

1n106

1lii  and  1lin

irin  and  1rin

n3106

Idea

处理区间 [l, r] 的问题,其中必然存在一个 i 使得 li=l ri=r ,否则不存在可行方案。

在找到 i 之后,处理子问题 [l, i-1][i+1, r]

对结果方案数的统计为: f(l,r)=Cilrlf(l,i1)f(i+1,r)

  • 对于如何快速获取 li=l ri=r 的 i ,利用哈希实现平均 O(1) 的查询。
  • 由于数据组数较多,每组数据对 HASH 的清空用 memset 是不可行的。用 VIS 数组表示对应 HASH 的第 i 位在第 VIS[i] 组样例时曾重写过。

Code

#include<bits/stdc++.h>
using namespace std;
const int RANGE = 40000010;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int HASH_RANGE = N*4;
int ica=1, n, idx, l[N], r[N], HASH[HASH_RANGE], VIS[HASH_RANGE], hashVal[N];
char buffer[RANGE];
bool flg = true;
inline bool read(int &ret) {
    char c;
    if(c=buffer[idx++], c=='\0') {    flg = false;    return 0;}
    while((c<'0' || c>'9') && c!='\0')    c = buffer[idx++];
    if(c == '\0')    {    flg = false;    return 0;    }
    ret = c-'0';
    while(c=buffer[idx++], c>='0'&&c<='9')    ret=ret*10+(c-'0');
    if(c == '\0')    {    flg = false;    return 0;    }
    return 1;
}
void hashInsert(int index) {
    int hash = hashVal[index];
    while(VIS[hash] == ica) {
        hash++;
        if(hash == HASH_RANGE)    hash = 0;
    }
    HASH[hash] = index;
    VIS[hash] = ica;
}
int hashGet(int hashValue, int left, int right) {
    int idx = hashValue;
    while(VIS[idx] == ica) {
            if(HASH[idx] >= left && HASH[idx] <= right && 
                l[HASH[idx]] == left && r[HASH[idx]] == right)
            return HASH[idx];
        idx++;
        if(idx == HASH_RANGE)    idx = 0;
    }
    return 0;
}
long long fac[N] = {1, 1}, inv[N] = {0, 1}, f[N] = {1, 1};
void init() {
    for(int i=2;i<N;i++) {
        fac[i] = fac[i-1] * i % mod;
        f[i] = (mod - mod/i) * f[mod%i] % mod;
        inv[i] = inv[i-1] * f[i] % mod;
    }
}
long long C(long long n, long long i) {
    if(i == 0 || i == n)    return 1;
    return fac[n] * inv[n-i] % mod * inv[i] % mod;
}
long long solve(int left, int right) {
    if(left > right)    return 1;
    int hashValue = left * 3 + right, pos;
    if(pos=hashGet(hashValue, left, right)) {
        long long ans = solve(left, pos-1);
        if(ans == 0)    return 0;
        ans *= solve(pos+1, right);
        if(ans == 0)    return 0;
        if(ans >= mod)    ans %= mod;
        ans *= C(right-left, pos-left);
        if(ans >= mod)    ans %= mod;
        return ans;
    } else {
        return 0;
    }
}
int main()
{
    int len = fread(buffer, 1, RANGE, stdin);    
    buffer[len] = '\0';
    init();
    while(read(n)!=false && flg)
    {
        for(int i=1;i<=n;i++)
            read(l[i]);
        for(int i=1;i<=n;i++)
            read(r[i]);
        for(int i=1;i<=n;i++)
        {
            hashVal[i] = l[i]*3 + r[i];
            hashInsert(i);    
        }
        printf("Case #%d: %lld\n", ica++, solve(1, n));
        fflush(stdout);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值