2023.8.5学习记录

牛客萌新赛5 - G

题目大意:给你一个长度为n的至少有n-1个不相同元素的序列,问你其中长度为k的子序列为多少,最终答案%1e9+7。

如果两个子序列长度不同,或者长度相同但存在位置i使得两个子序列b,c,bi!=ci成立,那么它们就是不同的。

考虑:当序列中没有相同元素时,相当于从n个数中拿k个数出来组成子序列。答案就是从C(k,n)。

若序列中有两个相同元素,那上面的计算必定会重复,什么情况下会重复呢,由上述定义可知,当两个相同元素中只选了

1个,并且剩下的k-1个都是在这两个元素旁边选的,最终情况下就会重复,而这种情况个数为C(k-1,n-(r-l+1))。

l和r为相同元素的左右下标,最终答案即为C(k,n)-C(k-1,n-(r-l+1))。


ll power(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) {
            ans *= a;
            ans %= mod;
        }
        a *= a;
        a %= mod;
        b >>= 1;
    }
    return ans;
}

const int M = 1e5 + 10;
struct Combinatorial_number{
    int fact[M], finv[M]; // fact[x]是x的阶乘,inv[x]是fact[x]的逆元 
    void init(){
        fact[0] = 1;
        for(int i = 1; i < (int)M; i ++){
            fact[i] = 1ll * fact[i - 1] * i % mod;
        }
        finv[M - 1] = power(fact[M - 1], mod - 2);
        for(int i = M - 2; i >= 0; i --){
            finv[i] = 1ll * finv[i + 1] * (i + 1) % mod;
        }
    }
    ll C(int n, int m){
        if(m < n || n < 0 || m < 0) return 0;
        return 1ll * fact[m] * finv[n] % mod * finv[m - n] % mod; 
    }
    ll A(int n, int m){
        if(m < n || n < 0 || m < 0) return 0;
        return 1ll * fact[m] * finv[m - n] % mod;
    }
    ll llC(int n, int m){
        if(m < n || n < 0 || m < 0) return 0;
        swap(n, m);
        long long sum = 1;
        if (m > n - m)  m = n - m;
        n = n - m + 1;
        for (int i = 1; i <= m; i ++){
            sum *= n ++;
            sum /= i;
        }
        return sum;
    }
};

struct Combinatorial_number comb;

void solve() {
    int n, k;
    cin >> n >> k;
    vector<int>a(n + 1);
    map<int, int>mp;
    int l = -1, r = -1;
    rep(i, 1, n) {
        cin >> a[i];
        if(mp[a[i]]) r = i, l = mp[a[i]];
        else mp[a[i]] = i;
    }
    ll ans = comb.C(k, n);
    if(r == -1) cout << ans << endl;
    else {
        int m = n - (r - l + 1);
        ans -= comb.C(k - 1, m);
        cout << (ans + mod) % mod<< endl;
    }
}
SP7579 YOKOF - Power Calculus

题目大意:给你若干个n(n<=1000),初始x,可以进行自乘或除(也可以乘除中间得到的结果),问x通过最少多少次操作可以变成x^n

多组输入,n为0的时候结束。

int n;
int stk[20];
int st;
bool dfs(int now, int m, int cnt) {
    if(now == n) return true;   //在规定步数内找到答案
    if(cnt >= m || now << (m - cnt) < n) {  //剪枝,超过规定次数 若最大次幂在规定次数自乘都无法到达n
        return false;
    }
    for (int i = st; i >= 1; i--) {  //乘:从大到小
        stk[++st] = now + stk[i];
        if(dfs(now + stk[i], m, cnt + 1)){
            return true;
        }
        --st;
    }
    for (int i = 1; i <= st; i++) { //除:从小到大
        if(stk[i] >= now) continue;
        stk[++st] = now - stk[i];
        if(dfs(now - stk[i], m, cnt + 1)){
            return true;
        }
        --st;
    }
    return false;
}

void solve() {
    stk[++st] = 1;
    while(cin >> n && n) {
        for (int i = 0; ; i++) {
            st = 1;
            if(dfs(1, i, 0)) {
                cout << i << endl;
                break;
            }
        }
    }
}
Addition Chains

题目大意 : 找一个与n有关的整数加成序列<a0...am>满足条件:

1.a0 = 1, 2. am = n, 3.序列严格递增, 4.对于每一个k(1~m),都有i, j(0~k-1,i,j可相等)使得a[i] + a[j] = a[k]

给定一个整数n(n <= 10000),找出符合上述四个条件的长度最小的整数加成序列。如果有多个满足要求的答案,只需要输出任意一个解即可。

要求最优的,bfs会爆空间,单纯的dfs会TLE,用迭代加深来做。

迭代加深

迭代加深搜索的本质还是深度优先搜索,只不过在搜索的同时带上了一个深度 ,当达到设定的深度时就返回。

一般用于找最优解。如果一次搜索没有找到合法的解,就让设定的深度加一,重新从根开始。

int ans[1000];
int n;
bool dfs(int now, int deg) {
    if(now > deg) {
        if(ans[deg] == n) return true;
        else return false;
    }
    for (int i = now - 1; i >= 1; i--) { //优化,要最短,则从大到小开始搜
        ans[now] = ans[now - 1] + ans[i];
        //剪枝:当序列以两倍递增时,最后的数为最大,若此时得到的最大值都小于n,则后面怎样都不会满足
        int temp = ans[now];
        for (int i = now + 1; i <= deg; i++) temp *= 2;  
        if(temp < n) return false;
        //直接返回
        if(dfs(now + 1, deg)) return true;
    }
    return false;
}

void solve() {
    ans[1] = 1;
    for (int i = 2; i <= n; i++) { 
        if(dfs(2, i)) { //找到就输出答案直接退出,因为最先找到的就是最优解
            cout << "1";
            for (int j = 2; j <= i; j++) {
                cout << " " << ans[j];
            }cout << endl;
            break;  
        }
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akb000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值