2015“嘉杰信息杯”中国大学生程序设计比赛(湖南)暨湘潭市第七届大学生程序设计比赛

题目链接

Problem A Coins

Duoxida buys a bottle of MaiDong from a vending machine and the machine give her n coins back. She places them in a line randomly showing head face or tail face on. And Duoxida wants to know how many situations that m continuous coins head face on among all possible situations. Two situations are considered different if and only if there is at least one position that the coins' faces are different.

题意:有N个硬币,每个硬币是正面或者反面朝上。问存在多少种方案,使得存在连续M个正面朝上的硬币。

思路:dp[i]表示前i个硬币满足条件的方案数,dp[i] = 2 * dp[i - 1] + (2^(i - m - 1) - dp[i - m - 1]),括号里的意思是到了第i个硬币的时候,新增的情况是第一次出现合法情况,即[i - m + 1,i]都是正面,i - m是反面,前i - m - 1个随便放,同时,随便放的时候可能产生有连续M个正面的,减去dp[i - m - 1]即可去掉这种情况。

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

const int N = 1000010;
const int mod = 1000000007;
long long p2[N],dp[N];

void solve() {
    int n,m;
    scanf("%d%d",&n,&m);
    memset(dp,0,sizeof(dp));
    dp[m] = 1;
    for(int i = m + 1; i <= n; i ++) {
        dp[i] = (2 * dp[i - 1] + p2[i - m - 1] - dp[i - m - 1]) % mod;
        if(dp[i] < 0) dp[i] += mod;
    }
    printf("%d\n",(int)dp[n]);
}

int main()
{
    p2[0] = 1;
    for(int i = 1; i < N; i ++) p2[i] = p2[i - 1] * 2 % mod;
    int t;
    scanf("%d",&t);
    while(t --) solve();
    return 0;
}

Problem B: Combination

题意:求 c(n,m) % (3^p) (1 < p <= 18)

思路:假设有一个多项式为P(n,x) = (x + 1) * (x + 2) * (x + 4) * .. * (x + n),其常数项为1 * 2 * 4 * 5 * 7 * ... * n(即 n!中缺少3的倍数)。

          则提取掉3之后,n! = P(n,x) * P(n / 3,x) * P(n / 9,x) * ... * P(0,0) % (3^p)。

          对于P(n,x),如果x是3的倍数,则x^e % (3^p) = 0,所以多项式的最高次数< p。       

          多项式的计算可以用分治,因为要求x是3的倍数,所以对于一个n,计算出 mid  = n / 2 之后,即P(mid,x) = (x + 1) * ... (x + mid - 1),

          mid + 1,2 * mid的结果是P([mid + 1,2 * mid],x) = (x + mid + 1) * ... * (x + 2 * mid - 1),利用P(mid,x)可以快速计算。

          然后P(2 * mid - 1,x) = P(mid,x) * P([mid + 1,2 * mid],x),从2 * mid + 1,n,剩余项不大于5像,暴力乘即可得到P(n,x)


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

const int N = 20;
long long c[N][N],mod;
const long long ut = 1LL << 62;
struct polynomial {
    int len;
    long long coe[N];
    polynomial() {
        len = 0;
        memset(coe,0,sizeof(coe));
    }
};

long long cal(long long n,long long m) {
    long long sum = 0;
    for(long long i = n / m; i > 0; i /= m)
        sum += i;
    return sum;
}

long long x,y;

long long ex_gcd(long long a,long long b)
{
    if(!b) {
        x = 1,y = 0;
        return a;
    }
    long long d = ex_gcd(b,a % b);
    long long tmp = x;
    x = y;
    y = tmp - (a / b) * y;
    return d;
}

long long ival(long long p) {
    ex_gcd(p,mod);
    if(x < 0) x += mod;
    return x;
}

polynomial get_polynomial(long long n,int p,int q) {
    //cout << n << " " << p << " " << q << endl;
    if(n + 1 <= 2 * p) {
        polynomial ans;
        ans.coe[0] = ans.len = 1;
        for(long long i = 1; i <= n; i ++) {
            if(i % p == 0) continue;
            long long x = i % mod;
            //sigma(coe[j] * x^j) * (x + i) = sigma((coe[j - 1] + coe[j] * i) * x^i)
            for(int j = ans.len; j > 0; j --) {
                ans.coe[j] = ans.coe[j - 1] + ans.coe[j] * x;
            }
            ans.coe[0] = ans.coe[0] * x % mod;
            if(ans.len < q) ans.len ++;
            for(int j = 0; j < ans.len; j ++)
                if(ans.coe[j] >= mod) ans.coe[j] %= mod;
        }
        return ans;
    }
    long long mid = (n + 1) / (2 * p) * p;
    polynomial ans1 = get_polynomial(mid - 1,p,q);
    long long pw[N];
    polynomial ans,ans2;
    ans2.len = ans1.len;
    pw[0] = 1;
    long long md = mid % mod;
    for(int i = 1; i < ans2.len; i ++) pw[i] = pw[i - 1] * md % mod;
    for(int i = 0; i < ans2.len; i ++) {
        for(int j = 0; j <= i; j ++) {
            ans2.coe[j] = ans2.coe[j] + c[i][j] * ans1.coe[i] % mod * pw[i - j];
        }
    }
    for(int i = 0; i < ans2.len; i ++)
        if(ans2.coe[i] >= mod) ans2.coe[i] %= mod; 
    for(int i = 0; i < ans1.len; i ++)
        for(int j = 0; j < ans1.len && i + j < q; j ++)
            ans.coe[i + j] = ans.coe[i + j] + ans1.coe[i] * ans2.coe[j];
    ans.len = min(q,ans1.len * 2);
    while(ans.len && !ans.coe[ans.len - 1]) ans.len --;
    if(ans.len == 0) ans.len ++;
    for(int i = 0; i < ans.len; i ++) ans.coe[i] %= mod;
    for(long long i = mid * 2 + 1; i <= n; i ++) {
        if(i % p == 0) continue;
        long long x = i % mod;
        //sigma(coe[j] * x^j) * (x + i) = sigma((coe[j - 1] + coe[j] * i) * x^i)
        for(int j = ans.len; j > 0; j --) {
            ans.coe[j] = ans.coe[j - 1] + ans.coe[j] * x;
        }
        ans.coe[0] = ans.coe[0] * x % mod;
        if(ans.len < q) ans.len ++;
        for(int j = 0; j < ans.len; j ++)
            if(ans.coe[j] >= mod) ans.coe[j] %= mod;
    }
    return ans;
}

long long fac(long long n,long long p,long long q) {
    long long ans = 1;
    //cout << n;
    while(n) {
        ans = ans * get_polynomial(n,p,q).coe[0] % mod;
        n /= p;
    }
    //cout << " " << p << " " << q << " " << ans << endl;
    return ans;
}

void solve() {
    long long n,m,p;
    cin >> n >> m >> p;
    if(n == m || m == 0) {
        cout << 1 << endl;
        return;
    }
    long long tmp = cal(n,3) - cal(m,3) - cal(n - m,3);
    if(tmp >= p) {
        cout << 0 << endl;
        return;
    }
    mod = 1;
    for(int i = 0; i < p; i ++) mod *= 3;
    long long ans = 1;
    while(tmp --) ans *= 3;
    ans = ans * fac(n,3,p) % mod * ival(fac(m,3,p)) % mod * ival(fac(n - m,3,p)) % mod;
    //cout << ans << " " << fac(n,3,p) << " " << fac(m,3,p) << " " << fac(n - m,3,p) << endl;
    cout << (ans + mod) % mod << endl;
}

int main()
{
    for(int i = 0; i < N; i ++) {
        c[i][i] = c[i][0] = 1;
        for(int j = 1; j < i; j ++)
            c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
    }
    int t;
    scanf("%d",&t);
    while(t --) solve();
    return 0;
}

Problem C CQRXLB

CQR and XLB are best friends. One night, they are staring each other and feel boring, and XLB says let's play game!

They place n piles of stones and take turns remove arbitrary amount(at least one) of stones in at least one pile at most x piles they chose. The one who can not remove any stone lose out.

CQR is a girl so she always move first. Duoxida wants to know who will win if they are both smart enough

题意:有N堆石子,两个人在玩游戏。游戏规则是可以取不超过x堆中任意石子数,至少取一个,不能取者败,问先手还是后手赢。

思路:nim变形,讲每堆石子数转化成二进制,然后求和,求和时要用x + 1进制不进位加法。判断最后是否结果为0即可。

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

int n,x;
int a[33];

void add(int tmp) {
    for(int i = 0; i < 32; i ++) {
        a[i] = (a[i] + tmp % 2) % (x + 1);
        tmp /= 2;
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t --) {
        scanf("%d%d",&n,&x);
        memset(a,0,sizeof(a));
        while(n --) {
            int y;
            scanf("%d",&y);
            add(y);
        }
        int sum = 0;
        for(int i = 0; i < 32; i ++) sum += a[i];
        if(sum) printf("CQR\n");
        else printf("XLB\n");
    }
    return 0;
}

Problem D Fraction

Everyone has silly periods, especially for RenShengGe. It's a sunny day, no one knows what happened to RenShengGe, RenShengGe says that he wants to change all decimal fractions between 0 and 1 to fraction. In addtion, he says decimal fractions are too complicate, and set that 1/3 is much more convient than 0.33333... as an example to support his theory.

So, RenShengGe lists a lot of numbers in textbooks and starts his great work. To his dissapoint, he soon realizes that the denominator of the fraction may be very big which kills the simplicity that support of his theory.

But RenShengGe is famous for his persistence, so he decided to sacrifice some accuracy of fractions. Ok, In his new solution, he confines the denominator in [1,1000] and figure out the least absolute different fractions with the decimal fraction under his restriction. If several fractions satifies the restriction, he chooses the smallest one with simplest formation.


题意:给出一个(0,1)之间的分数,找出一个分数a/b,其中0 < a <= b <= 1000,满足它们的绝对值之差最小。

思路:分母不大于1000,直接预处理出所有的a/b,gcd(a,b) = 1,排序。对于输入的每个分数,直接二分查找,最接近的肯定是不小于它的最小的那个分数或者不大于它的最大的那个分数

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;

const int N = 1010;
struct point
{
    int x,y;
    double z;
    friend bool operator < (const point &p,const point &q) {
        return p.z < q.z;
    }
}a[N * N];

int main()
{
    int cnt = 0;
    for(int i = 1; i <= 1000; i ++) {
        for(int j = 0; j <= i; j ++) {
            if(__gcd(i,j) != 1) continue;
            a[cnt].x = j,a[cnt].y = i,a[cnt].z = 1.0 * j / i;
            cnt ++;
        }
    }
    sort(a,a + cnt);
    int t;
    scanf("%d",&t);
    while(t --) {
        double x;
        scanf("%lf",&x);
        int idx = cnt - 1,lt = 0,rt = cnt - 1,mid;
        while(lt <= rt) {
            mid = lt + rt >> 1;
            if(a[mid].z >= x) {
                idx = mid;
                rt = mid - 1;
            }
            else lt = mid + 1;
        }
        if(fabs(a[idx - 1].z - x) < fabs(a[idx].z - x)) {
            printf("%d/%d\n",a[idx - 1].x,a[idx - 1].y);
        }
        else printf("%d/%d\n",a[idx].x,a[idx].y);
    }
    return 0;
}

Problem F Segment Tree

A contest is not integrity without problems about data structure.

There is an array a[1],a[2],…,a[n]. And q questions of the following 4 types:
  • 1 l r c - Update a[k] with a[k]+c for all l≤k≤r
  • 2 l r c - Update a[k] with min{a[k],c} for all l≤k≤r;
  • 3 l r c - Update a[k] with max{a[k],c} for all l≤k≤r;
  • 4 l r - Ask for min{a[k]:l≤k≤r} and max{a[k]:l≤k≤r}.
题意:如题

思路:常规线段树,增加两个域记录区间的上界和下界即可。

<pre name="code" class="cpp">#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 200010;
struct tree
{
    int lt,rt,det,maxn,mii,flag;
}a[N << 2];

int c[N];
void init(int lt,int rt,int step) {
    a[step].lt = lt;
    a[step].rt = rt;
    a[step].det = 0;
    if(lt == rt) {
        a[step].maxn = a[step].mii = c[lt];
        return;
    }
    int mid = lt + rt >> 1;
    init(lt,mid,2 * step);
    init(mid + 1,rt,2 * step + 1);
    a[step].maxn = max(a[step * 2].maxn,a[2 * step + 1].maxn);
    a[step].mii = min(a[step * 2].mii,a[2 * step + 1].mii);
}

void add(int step,int val) {
    a[step].maxn += val;
    a[step].mii += val;
    a[step].det += val;
}

void down(int step) {
    add(2 * step,a[step].det);
    add(2 * step + 1,a[step].det);
    a[step].det = 0;
    a[2 * step].maxn = min(a[step].maxn,a[2 * step].maxn);
    a[2 * step].maxn = max(a[step].mii,a[2 * step].maxn);
    a[2 * step].mii = max(a[step].mii,a[2 * step].mii);
    a[2 * step].mii = min(a[step].maxn,a[2 * step].mii);
    
    a[2 * step + 1].maxn = min(a[step].maxn,a[2 * step + 1].maxn);
    a[2 * step + 1].maxn = max(a[step].mii,a[2 * step + 1].maxn);
    a[2 * step + 1].mii = max(a[step].mii,a[2 * step + 1].mii);
    a[2 * step + 1].mii = min(a[step].maxn,a[2 * step + 1].mii);
}

void add(int lt,int rt,int step,int val) {
    if(a[step].lt == lt && a[step].rt == rt) {
        add(step,val);
        return;
    }
    down(step);
    if(rt <= a[2 * step].rt) add(lt,rt,2 * step,val);
    else if(lt > a[2 * step].rt) add(lt,rt,2 * step + 1,val);
    else {
        add(lt,a[2 * step].rt,2 * step,val);
        add(a[2 * step + 1].lt,rt,2 * step + 1,val);
    }
    a[step].maxn = max(a[step * 2].maxn,a[2 * step + 1].maxn);
    a[step].mii = min(a[step * 2].mii,a[2 * step + 1].mii);
}

void update_min(int lt,int rt,int step,int val) {
    if(a[step].lt == lt && a[step].rt == rt) {
        a[step].flag = 1;
        a[step].mii = max(val,a[step].mii);
        a[step].maxn = max(val,a[step].maxn);
        //update(step);
        return;
    }
    down(step);
    if(rt <= a[2 * step].rt) update_min(lt,rt,2 * step,val);
    else if(lt > a[2 * step].rt) update_min(lt,rt,2 * step + 1,val);
    else {
        update_min(lt,a[2 * step].rt,2 * step,val);
        update_min(a[2 * step + 1].lt,rt,2 * step + 1,val);
    }
    a[step].maxn = max(a[step * 2].maxn,a[2 * step + 1].maxn);
    a[step].mii = min(a[step * 2].mii,a[2 * step + 1].mii);
}

void update_max(int lt,int rt,int step,int val) {
    if(a[step].lt == lt && a[step].rt == rt) {
        a[step].flag = 1;
        a[step].maxn = min(val,a[step].maxn);
        a[step].mii = min(val,a[step].mii);
        //update(step);
        return;
    }
    down(step);
    if(rt <= a[2 * step].rt) update_max(lt,rt,2 * step,val);
    else if(lt > a[2 * step].rt) update_max(lt,rt,2 * step + 1,val);
    else {
        update_max(lt,a[2 * step].rt,2 * step,val);
        update_max(a[2 * step + 1].lt,rt,2 * step + 1,val);
    }
    a[step].maxn = max(a[step * 2].maxn,a[2 * step + 1].maxn);
    a[step].mii = min(a[step * 2].mii,a[2 * step + 1].mii);
}

void query(int lt,int rt,int step,int &mx,int &mi) {
    if(a[step].lt == lt && a[step].rt == rt) {
        mx = max(mx,a[step].maxn);
        mi = min(mi,a[step].mii);
        return;
    }
    down(step);
    if(rt <= a[2 * step].rt) query(lt,rt,2 * step,mx,mi);
    else if(lt > a[2 * step].rt) query(lt,rt,2 * step + 1,mx,mi);
    else {
        query(lt,a[2 * step].rt,2 * step,mx,mi);
        query(a[2 * step + 1].lt,rt,2 * step + 1,mx,mi);
    }
}

void solve() {
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i ++) scanf("%d",&c[i]);
    init(1,n,1);
    int cd,x,y,z;
    while(m --) {
        scanf("%d%d%d",&cd,&x,&y);
        if(cd == 4) {
            int mx = -0x7fffffff;
            int mi = -mx;
            query(x,y,1,mx,mi);
            printf("%d %d\n",mi,mx);
        }
        else if(cd == 1) {
            scanf("%d",&z);
            add(x,y,1,z);
        }
        else if(cd == 3) {
            scanf("%d",&z);
            update_min(x,y,1,z);
        }
        else {
            scanf("%d",&z);
            update_max(x,y,1,z);
        }
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t --) solve();
    return 0;
}


 

Problem String Maze

Loying likes string problems very much. Today, he go to the park with his girl friends and find a place called 'String Maze' where he have never been before. The special name soon interests him and his blood is flowed from his lower part of body to the brain.

So, he leave his girl friends and approach the maze. The maze consists of n nodes and m tunels and each tunel is assigned a lowercase letter. The nodes are numbered from 1 to n. Node 1 is the entrance and node n is the exit. Every tunel will cost him 1 minute. In order to enjoy himself to the full, he needs playing at least L minutes. But if he makes her girl friends wait more than R minutes, maybe he will be in a nice boat. In addition, he wants to show his string ability in a way that the character of tunels he passed in order is a plalindrome string.

He wants to have great fun, not irritate his girl friends and also show his string power. How many different paths can he achieve his goals?

题意:给出N个点,M条边,每条边都有一个字母标志,边长都是1,问从1走到n,存在多少条回文路径,且长度为[L,R]

思路:对于边长为偶数的,存在一个中心点,中心点往两边扩展出回文路径。用a(i,j)表示从中心出发,往前走到了i点,往后走到了j点,下次枚举两个点k,r,看边(i,k)和边(j,r)对应的字母是否相等,如果是,即可转移。最后的结果a(1,n)即为所求。

          边长为奇数的,可以看成有两个中心,即1条边。其他情况同偶数。

          这题需要注意的是有点卡常数。

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

const int N = 85;
const long long mod = 1000000007;
const long long ut = 1LL << 62;
int size;
struct matrix {
    long long a[N][N];
    matrix() {
        memset(a,0,sizeof(a));
    }
    matrix operator * (const matrix &p) {
        matrix c;
        for(int i = 0; i <= size; i ++) {
            for(int k = 0; k <= size; k ++) {
                if(!a[i][k]) continue;
                for(int j = 0; j <= size; j ++) {
                    c.a[i][j] = c.a[i][j] + a[i][k] * p.a[k][j];
                    if(c.a[i][j] >= ut) c.a[i][j] %= mod;
                }
            }
        }
        for(int i = 0; i <= size; i ++)
            for(int j = 0; j <= size; j ++)
                if(c.a[i][j] >= mod) c.a[i][j] %= mod;
        return c;
    }
    matrix operator + (const matrix &p) {
        matrix c;
        for(int i = 0; i <= size; i ++) {
            for(int j = 0; j <= size; j ++) {
                c.a[i][j] = a[i][j] + p.a[i][j];
                if(c.a[i][j] >= mod) c.a[i][j] -= mod;
            }
        }
        return c;
    }
}f[70];

matrix matrix_pow(matrix a,long long n) {
    matrix b;
    for(int i = 0; i <= size; i ++) b.a[i][i] = 1;
    for(int i = 0; i <= 60; i ++)
        if(n & (1LL << i)) b = b * f[i];
    return b;
}                   
                    
void solve() {
    int n,m;
    long long L,R;
    cin >> n >> m >> L >> R;
    int maps[10][10],cnt = 0;
    for(int i = 0; i < n; i ++)
        for(int j = 0; j < n; j ++)
            maps[i][j] = cnt ++;
    int edges[10][10];
    memset(edges,-1,sizeof(edges));
    char s[3];
    int x,y;
    while(m --) {
        scanf("%d%d%s",&x,&y,s);
        x --, y --;
        edges[x][y] = edges[y][x] = s[0];
    }
    matrix a;
    size = cnt;
    for(int i = 0; i < n; i ++)
        for(int j = 0; j < n; j ++)
            for(int k = 0; k < n; k ++)
                for(int r = 0; r < n; r ++)
                    if(edges[i][k] != -1 && edges[i][k] == edges[j][r])
                        a.a[maps[k][r]][maps[i][j]] ++;
    for(int i = 0; i < cnt; i ++)
        a.a[cnt][i] = a.a[maps[0][n - 1]][i];
    a.a[cnt][cnt] = 1;
    f[0] = a;
    for(int i = 1; i <= 60; i ++) f[i] = f[i - 1] * f[i - 1];
    long long ans = 0;
    //get_even
    if(R >= 2) {
        long long st = (L - 1) / 2,ed = R / 2;
        matrix b = matrix_pow(a,ed);
        for(int i = 0; i < n; i ++)
            ans += b.a[cnt][maps[i][i]];
        if(st) {
            matrix c = matrix_pow(a,st);
            for(int i = 0; i < n; i ++)
                ans -= c.a[cnt][maps[i][i]];
        }
        ans = (ans % mod + mod) % mod;
    }
    //get_odd;
    long long st = L / 2 - 1,ed = (R + 1) / 2 - 1;
    if(ed) {
        matrix b = matrix_pow(a,ed);
        for(int i = 0; i < n; i ++)
            for(int j = 0; j < n; j ++)
                if(i != j && edges[i][j] != -1) ans += b.a[cnt][maps[i][j]];
        ans += b.a[cnt][cnt];
        //cout << ans << endl;
        if(st > 0) {
            matrix c = matrix_pow(a,st);
            for(int i = 0; i < n; i ++)
                for(int j = 0; j < n; j ++)
                    if(i != j && edges[i][j] != -1) ans -= c.a[cnt][maps[i][j]];
            ans -= c.a[cnt][cnt];
        }
        else if(st == 0 && edges[0][n - 1] != -1) ans --;
    }
    else if(edges[0][n - 1] != -1) ans ++;
    ans = (ans % mod + mod) % mod;
    printf("%d\n",(int)ans);
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t --) solve();
    return 0;
}


Problem H Ternary

题意:有个无限长的循环节为m的数组,a[0],a[1]...,a[m - 1],当 i >= m的时候,a[i] = a[i % m]
          有个操作,b(1,i) = (a[i] + a[i + 1])% 3,b(2,i) = (b(1,i) + b(1,i + 1))%3,...,b(n,i) = (b(n,i) + b(n,i + 1))%3,

          给出n,m,求b(n,i) (0 <= i < m)

思路:b(1,i) = a[i] + a[i + 1],b(2,i) = b(1,i) + b(1,i + 1) = a[i] + a[i + 1] * 2 + a[i + 2],

          可以归纳得到 b(n,i) = (c(n,0) * a[i] + c(n,1) * a[i + 1] + ... + c(n,n) * a[i + n])%3,c(n,i)为组合数

          当n=3^k的时候,c(n,0) = c(n,n) = 1。由lucas定理,c(n,i) % 3 = c(n / 3,i / 3) % 3 * c(n % 3,i % 3)。n除了最高位外,其他为%3为0,而i的最高位%3必不为0,且i的最高位位置小于n的最高位位置(0 < i < n),所以c(0,3进制下x的最高位) = 0,因此c(n,i) % 3 = 0。

          因此当取步长为3^k的时候,除了a[i]和a[i + 3^k],其他的系数都是0,因此每次步长为3^k暴力迭代即可。

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

const int N = 20010;
int a[N],b[N];
char s[N];

int main()
{
    int t;
    scanf("%d",&t);
    while(t --) {
        long long n,m;
        cin >> m >> n;
        scanf("%s",s);
        for(int i = 0; i < m; i ++) a[i] = s[i] - '0';
        while(n) {
            long long tmp = 1;
            while(tmp * 3 <= n) tmp *= 3;
            for(int i = 0; i < m; i ++)
                b[i] = (a[i] + a[(i + tmp) % m]) % 3;
            for(int i = 0; i < m; i ++) a[i] = b[i];
            n -= tmp;
        }
        for(int i = 0; i < m; i ++) printf("%d",a[i]);
        printf("\n");
    }
    return 0;
}

Problem I Tuples

It is very diffcult to write a good problem description for Duoxida and her bad English. So, A nice story is replaced by the formalized math description.

Give two integers n, m.

How many tuples (a1, a2, a3, ..., am) satisfied that 0 < a1 < a2 < ... < am and a1 + a2 + a3 + ... + am = n.

题意:给出n,m,问存在多少种方案,满足a[1] + a[2] + .. + a[m] = n,且a[1] < a[2] < ... < a[m]

思路:因次数是递增的,且全是正数,因此m * (m + 1) / 2 <= n,一旦不满足不等式,结果必为0.同时,在这条式子的限制下,m不会大于446

          将a[i] - (i - 1),则每个数变成了非递减,即a[1] <= a[2] <= ... <= a[m]

          用dp[i][j]表示前i个数和为j的方案数,则dp[i][j] = dp[i][j - i] + dp[i - 1][j - 1],意思是1)dp[i][j] += dp[i][j - i],即给每个数加1,2)或者新增加一个为1的数,dp[i][j] += dp[i - 1][j - 1]

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

vector<int> val[510];
vector<int> id[510];
const int N = 100010;
int dp[2][N],ans[N * 10];
const int mod = 1000000007;
//dp[i][j] = dp[i][j - i] + dp[i - 1][j - 1]
int main()
{
    int t;
    scanf("%d",&t);
    for(int i = 1; i <= t; i ++) {
        int n,m;
        scanf("%d%d",&n,&m);
        if(m > 500 || m * (m + 1) / 2 > n) continue;
        val[m].push_back(n - m * (m - 1) / 2);
        id[m].push_back(i);
    }
    dp[0][0] = 1;
    for(int i = 1; i <= 500; i ++) {
        int flag = i & 1;
        for(int j = 0; j < N; j ++) dp[flag][j] = 0;
        for(int j = i; j < N; j ++) {
            dp[flag][j] = dp[flag ^ 1][j - 1] + dp[flag][j - i];
            if(dp[flag][j] >= mod) dp[flag][j] -= mod;
        }
        for(int j = 0; j < val[i].size(); j ++) {
            ans[id[i][j]] = dp[flag][val[i][j]];
            //cout << i << " " << id[i][j] << " " << val[i][j] << " " << dp[flag][val[i][j]] << " " << dp[1][3] << endl;
        }
    }
    for(int i = 1; i <= t; i ++) printf("%d\n",ans[i]);
    return 0;
}

Problem J Yada Number

Every positive integer can be expressed by multiplication of prime integers. Duoxida says an integer is a yada number if the total amount of 2,3,5,7,11,13 in its prime factors is even.

For instance, 18=2 * 3 * 3 is not a yada number since the sum of amount of 2, 3 is 3, an odd number; while 170 = 2 * 5 * 17 is a yada number since the sum of amount of 2, 5 is 2, a even number that satifies the definition of yada number.

Now, Duoxida wonders how many yada number are among all integers in [1,n].

题意:问1[,n]区间中,有多少个数,它的2,3,5,7,11,13的这几个因子数目之和为偶数

思路:预处理出所有的x,满足x只含有2,3,5,7,11,3这几个质因子,且数目为偶数。x的数目略大于10000

          对于一个数n,枚举所有的x,对于一个x,f(n/x)即求出[1,n/x]中不含有2,3,5,7,11,13作为因子的数有多少个,这个是经典的容斥问题。对所有的f(n/x)求和即可

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

int a[] = {2,3,5,7,11,13};

int cal(int n) {
    int ans = 0;
    for(int i = 0; i < 64; i ++) {
        int tmp = 1;
        for(int j = 0; j < 6; j ++)
            if(i & (1 << j)) {
                tmp *= a[j];
                tmp *= -1;
            }
        ans += n / tmp;
    }
    return ans;
}

const int maxn = 1000000000;
int b[1000010];
int tot;
void pre_process(long long x,int pos,int flag) {
    if(pos == 6) {
        if(!flag) b[++ tot] = x;
        return;
    }
    while(x <= maxn) {
        pre_process(x,pos + 1,flag);
        x *= a[pos];
        flag ^= 1;
    }
}

void solve() {
    int n;
    scanf("%d",&n);
    int ans = 0;
    for(int i = 1; i <= tot && b[i] <= n; i ++) {
        ans += cal(n / b[i]);
    }
    printf("%d\n",ans);
}

int main()
{
    pre_process(1,0,0);
    sort(b + 1,b + tot + 1);
    int t;
    scanf("%d",&t);
    while(t --) solve();
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值