18.8.31 考试总结

这道题就是打表找规律 然而我打表并没有找到规律... yyyuuudalao就找到了tqltql%%%

打表发现 $ A $ 数列是这样子1 1 ,2 ,2 ,6

1 1 2 2 3 4 4 4 5 6 6 7 8 8 8 8 9 10 10 11 12 12 12 13 14 14 15 16 16 16 16 16...

$ t[i] $ 表示 $ i $ 这个数出现次数 然后发现 $t[i]$ 为 $i$ 所含的因子 $2$ 个数 $+ 1$ ($1$除外)

也就是说每个数如果多一个 $2$ 那么他就会多出现一次 并且这些数呈等差数列

然后就可以通过除以 $2$ 的次幂来计算这些多出现的数的和

现在的问题就变成了只要知道这个地方的数是什么 就可以求出它的前缀和 那么问题就是给你一个位置

让你求出这个位置上的值是什么 同样地方法 可以求出某个数及他前面共有多少个数 并且这玩意儿是递增的

二分就可以了 然后注意不要爆long long 了

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll mod = 1e9 + 7;
ll L,R,inv = 500000004;

ll find_sum(ll x) {

    ll kk = 1,num = 0;
    while(kk <= x) {
        num += x / kk;
        if(num > 1e18) break;
        kk *= 2;
    }
    return num + 1;
}

ll dc(ll a,ll b,ll s) {

    return (a + b) % mod * s % mod * inv % mod;
}

ll solve(ll x) {
    if(x == 0) return 0;
    if(x == 1) return 1;
    if(x == 2) return 2;
    ll l = 1,r = x,ax = x - 1;
    while(l <= r) {
        ll mid = (l + r) >> 1;
        ll ss = find_sum(mid);
        if(ss < x) ax = mid,l = mid + 1;
        else r = mid - 1;
    }
    ll k = 1,ans = 1;
    while(k <= ax) {
        ans = (ans + dc(k,ax / k * k,(ax / k)% mod)) % mod;
        k *= 2;
    }
    ll ss = find_sum(ax);
    ans = (ans + (ax + 1) % mod * (x - ss) % mod) % mod;
    return ans;
}

int main( ) {

    freopen("me.in","r",stdin);
    freopen("me.out","w",stdout);
    scanf("%I64d%I64d",& L,& R);
    cout << (solve(R) - solve(L - 1) + mod) % mod << endl;
}

这道题是一道dp 神tmyyyuuudalao又做出来了tqltqltql%%%%%%%%%%

$dp[i][j]$ 表示此时填充了 $i$ 个数 利润为 $j$ 的方案数 那么在转移之前需要知道几个性质

1.我们从小到大放数进去 这样子每次进来的数所产生的收益肯定是他到两边界的 $min$ 值

2.区间之间的合并是互不影响的 因为原左区间的右边界相当于现在放进去的最大值 右区间的左边界也是一样的(从小往大放)

3.一个区间一定的不管它放什么数所产生的方案数都是一样的因为他们的相对大小是一样的 区间内没有重复的数

4.别看题目给你的x那么大 它实际的x是非常小的 因为 $n$ 小于80 每次最多要这个区间的一半 区间每次减半 所以不会超过300

所以就可以转移了

$dp[i][k] += ∑(dp[j][s] * dp[i - j][k - s] * c[i][j])$然后枚举 $j$ 和 $s$ 就可以了 $c[i][j]$ 表示组合数C $i$ 个里面选 $j$ 个

(因为对于这个玩意儿它是可以乱放的 方案数不受影响)

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll mod = 998244353;
ll dp[90][300],c[100][100],p[105],inv_p[105];
int x,ma[200],n;

void init( ) {

    c[0][0] = 1;
    for(int i = 0;i <= n;i ++)
      for(int j = 0;j <= i;j ++) {
          if(j == 0 || i == j) c[i][j] = 1;
          else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
    }
}

void solve( ) {

    dp[0][0] = 1;
    dp[1][1] = 1; ma[1] = 1;
    for(int i = 2;i <= n;i ++)
       for(int j = 0;j < i;j ++) {
             for(int s = j;s <= ma[j];s ++)
               for(int k = i - j - 1;k <= ma[i - j - 1];k ++) {
                   int cc = min(i - j,j + 1);
                   ma[i] = max(ma[i],s + k + cc);
                   dp[i][s + k + cc] = (dp[i][s + k + cc] + c[i - 1][j] * dp[j][s] % mod * dp[i - j - 1][k]) % mod;
            }
       }
    printf("%I64d",dp[n][x]);
}

int main( ) {

    freopen("good.in","r",stdin);
    freopen("good.out","w",stdout);
    scanf("%d%d",& n,& x);
    init( );
    solve( );
}

这道题第一眼望过去 哎呀这不是背包吗嘻嘻嘻 然后定睛一看x范围是1e18...

行吧我走 :))

因为 $A$ 的范围是非常小的 所以可以考虑余数

对于所有物品装满 $x$ 的容量 可以知 $x = q1 * a[1] + q2 * a[2] + q3 * a[3]...qn * a[n]$

所以假设我们剔除 $a[1]$ 剩下的物品装的容量模 $a[i]$ 肯定与 $x$ 模 $a[i]$ 同余

那么考虑 $dp[i]$ 为剔除 $a[1]$ 之后剩下的物品所装的容量模上 $a[1]$ 余数为i的情况是否可以达到

那么剩下的空间肯定是可以通过装 $a[1]$ 来填充满的

然而这时候会出现一个问题 比如我的a是 4 6 8

然而我想要装2 的容量 而6模上4为2 所以在我们原先的设定中这个是合法的 但是这个并不合法

所以考虑 $dp[i]$ 表示能够装满达到状态的最少花费是多少 如果 $dp[i] > x$ 那么不管怎么样我们都是凑不到x的

这个转移可以用spfa搞

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll dis[100000 + 5],cmp,x;
int n,q,a[10005];
bool vis[100005];
queue<int>Q;

void SPFA( ) {

    vis[0] = true;
    memset(dis,0x3f3f3f,sizeof(dis));
    dis[0] = 0;
    cmp = dis[3];
    Q.push(0);
    while(! Q.empty( )) {
        int cur = Q.front( ); Q.pop( );
        vis[cur] = false;
        for(int i = 2;i <= n;i ++) {
            int v = (cur + a[i]) % a[1];
            if(dis[v] > dis[cur] + a[i]) {
                dis[v] = dis[cur] + a[i];
                if(! vis[v]) {
                    vis[v] = true;
                    Q.push(v);
                }
            }
        }
    }
}

int main( ) {

    freopen("hungry.in","r",stdin);
    freopen("hungry.out","w",stdout);
    scanf("%d%d",& n,& q);
    for(int i = 1;i <= n;i ++) scanf("%d",& a[i]);
    sort(a + 1,a + n + 1);
    SPFA( );
    while(q --) {
        scanf("%I64d",& x);
        if(dis[x % a[1]] == cmp || dis[x % a[1]] > x) printf("-1\n");
        else printf("1\n");
    }
}

原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9567598.html

时间: 2024-10-29 19:39:26

18.8.31 考试总结的相关文章

18.03.31 顺时针旋转90&#176;24位真彩色bmp图片

1 #include <iostream> 2 #include <fstream> 3 #include <fstream> 4 #include <Windows.h> 5 6 using namespace std; 7 8 int main(int argc,char*argv[]) 9 { 10 tagBITMAPFILEHEADER a;//头信息块 11 tagBITMAPINFOHEADER b;//描述块 12 ifstream fin(a

2016.3.31考试心得

周四. 第一节C语言上机考试,内容:结构体. 要注意分清定义类型和结构体嵌套之后调用变量的书写. 要机智的将 之前学过的内容与结构体相融合,例如函数 排序等. 结构体输入一个字符串的时候%s后不要加空格,否则可能导致运行错误没法输入. 总之 还要勤加练习. 2014.4.4

18.1.1考试

今天考的题目都是DP,本来以为会有什么图论...根据今天题目比较水的原因,我直接放解题报告,大家应该可以看得懂.. T1 Source #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> using namespace std; const int maxn=10000+10,maxm=10010; #define file(a) freopen(a".i

18.8.13 考试总结

1.1 问题描述请构造一颗n 个节点的树,使得其价值最大.f(d) 表示树上,度数为d 的一个点能够获取的价值.这棵树的价值为Σni=1 f(di)di 表示第i 个点的度数1.2 输入第一行一个整数T,接下来T 组数据,每组数据输入两行.第一行输入整数n.第二行输入n ?? 1 个整数f(i) 代表f(1) f(n ?? 1).1.3 输出对于每组数据输出一行,为能够构造的树的最大价值. 一开始我以为是一道树形dp... 考完了才知道原来这个是一道背包问题 n个节点 总共有2n - 2个度数

18.8.26 考试总结

我真的服了 我考试的时候这道题题都是读错了的 交了个挖挖机结果还狗了20分.. 这道题是一道找规律的题 看完题很显然能够发现我们可以将相同颜色的连通块缩点 因为同一个联通块的可以一次操作全部变成另外一种颜色 所以就缩点就好了.. 对于缩点后的一条链 每次我们可以将一个点变色 那么和他相邻的点就和他颜色一样 然后就再次缩点 所以每次链的长度都可以 -2 所以说最后缩点的次数就是  点的个数 / 2  (下取整) 但是这是对于一条链 那么对于一棵树而言呢? 其实是一样的 因为每次搞这个操作 我们都可

18.9.22 考试总结

这道题一看就是可持久化并查集 然后我就愉快的yy了一波 还是错掉了qwqwqwqwq 方法是对的 就是我每次在树上查询$fa$的时候我还压缩了路径 导致这玩意空间炸掉了 所以要保证时间复杂度 就启发式合并 也就是$size$小的往$size$大的搞 这样子就保证每次合并的时候连通块元素个数每次至少乘以$2$ 也就是保证了层数是$log$级的 代码 #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int n

18.10.17 考试总结

今天心态崩崩,,,.. 这道题比较简单 因为每一位是单独对答案产生贡献的 所以枚举每一位 用数位dp求出该位是$1$的数量 在求出该位是$0$的 那么答案就是那一位对应的二的幂次再乘以$num1 * num0 * 2$ 每一对会产生两次贡献 代码 #include <bits/stdc++.h> #define rg register #define il inline using namespace std; typedef long long ll; const ll MOD = 1e9

2019.10.28~2019.10.31考试总结

连续几天的考试都没有总结,今天有些时间,总结一下这几场考试的情况. 10.28场 开场先读题,然后推了推T2,发现是水题.出于对期望的恐惧,没有选择研究T3,而是回头肝T1,想了很久,没有思路.打完T2,自己搓了一些数据,过了,回头打了T1的暴搜,再打了一个表,似乎部分分挺多的.再做T3,有点思路,但思路并不清晰,于是打了特殊数据就完了. 期望得分:70+100+20=190 最终得分:30+100+20=150 结果T1是DP,没看出来.唔...原题,地精部落,题果然还是做的太少了,有些人做过

杭电OJ 2010 2014年9月24日18:29:31

水仙花数 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 97954    Accepted Submission(s): 29126 Problem Description 春天是鲜花的季节,水仙花就是其中最迷人的代表,数学上有个水仙花数,他是这样定义的:“水仙花数”是指一个三位数,它的各位数字的立方和等于其本身,比如:153=1^3+