Fair Distribution(整除分块)

Fair Distribution

有n个机器人,m个能量条,能够进行两种操作,n–或m++,每次操作花费1美元,求使得m%n=0的最小花费
设 n 减 少 了 x , 则 花 费 为 n − ( n − x ) + ( [ m − 1 n − x ] + 1 ) ∗ ( n − x ) − m = n − m + [ m − 1 n − x ] ∗ ( n − x ) , m i n ( [ m − 1 n − x ] ∗ ( n − x ) ) 可 以 用 整 除 分 块 O ( n ) 来 求 得 。 对 于 [ n i ] 设n减少了x,则花费为 n-(n-x)+( [\frac{m-1}{n-x}]+1)*(n-x)-m= n-m+[\frac{m-1}{n-x}]*(n-x),min([\frac{m-1}{n-x}]*(n-x))可以用整除分块O(\sqrt n)来求得。对于[\frac{n}{i}] nxn(nx)+([nxm1]+1)(nx)m=nm+[nxm1](nx),min([nxm1](nx))O(n )[in], 显 然 会 有 很 多 i 的 值 是 相 同 的 , 那 么 把 这 些 i 分 成 一 块 , 只 用 求 一 次 就 行 了 显然会有很多i的值是相同的,那么把这些i分成一块,只用求一次就行了 ii , 但 是 要 知 道 这 个 块 的 起 始 位 置 , 开 始 位 置 是 我 们 要 枚 举 的 , 结 束 位 置 为 n / ( n / i ) . ,但是要知道这个块的起始位置,开始位置是我们要枚举的,结束位置为n/(n/i). n/(n/i).

#include <iostream>
#include <cstring>
#include <cmath>
#include <bitset>
#include <queue>
#include <vector>
#include <cstdio>
#include <set>
#include <sstream>
#include <cstring>
#include <algorithm>
#include <map>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define reps(i, a, b) for (int i = a; i >= b; i--)
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a*b/gcd(a,b)
#define ls(node) tree[node].ls
#define rs(node) tree[node].rs
#define val(node) tree[node].val
using namespace std;
const int N = 1e6+ 7;
const int M = 50+7;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
typedef long long ll;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        int ans=inf;
        scanf("%d%d",&n,&m);
        if(n>=m)
        {
            printf("%d\n",n-m);
        }
        else
        {
           for(int l=1,r;l<=n;l=r+1){
                r=min(n,(m-1)/((m-1)/l));
                ans=min(ans,(m-1)/l*l);
           }
           printf("%d\n",ans+n-m);
        }
    }
    return 0;
}

其他可以用整除分块的题目

约数研究
看起来和整除分块没啥关系,因为一眼看过去根本没有 ∑ n i \sum\frac{n}{i} in,但是 ∑ f ( i ) 是 可 以 转 化 成 ∑ n i \sum f(i)是可以转化成\sum\frac{n}{i} f(i)in的,
∑ 1 n f ( i ) = ∑ i = 1 n i 的 约 数 , i 的 约 数 = ∑ 1 n [ j ∣ i ] , 即 能 被 i 整 除 的 数 的 个 数 , = ∑ i = 1 n ∑ j = 1 n [ j ∣ i ] = ∑ j = 1 n ∑ i = 1 n [ j ∣ i ] , 这 里 就 变 成 了 能 整 除 j 的 个 数 , 那 么 显 然 1 − n 中 能 整 除 j 的 个 数 为 n j , 所 以 原 式 化 为 ∑ n j , 然 后 就 可 以 用 整 除 分 块 了 。 \sum_1^n f(i)=\sum_{i=1}^ni的约数,i的约数=\sum_1^n[j|i],即能被i整除的数的个数,=\sum_{i=1}^{n}\sum_{j=1}^{n}[j|i]=\sum_{j=1}^{n}\sum_{i=1}^{n}[j|i],这里就变成了能整除j的个数,那么显然1-n中能整除j的个数为\frac{n}{j},所以原式化为\sum\frac{n}{j},然后就可以用整除分块了。 1nf(i)=i=1ni,i=1n[ji],i=i=1nj=1n[ji]=j=1ni=1n[ji],j1njjn,jn,

#include <iostream>
#include <cstring>
#include <cmath>
#include <bitset>
#include <queue>
#include <vector>
#include <cstdio>
#include <set>
#include <sstream>
#include <cstring>
#include <algorithm>
#include <map>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define reps(i, a, b) for (int i = a; i >= b; i--)
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a*b/gcd(a,b)
#define ls(node) tree[node].ls
#define rs(node) tree[node].rs
#define val(node) tree[node].val
using namespace std;
const int N = 1e6+ 7;
const int M = 50+7;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
typedef long long ll;
int main()
{
    int n;
    scanf("%d",&n);
    int ans=0;
    for(int l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        ans+=(r-l+1)*(n/l);
    }
    printf("%d\n",ans);
    return 0;
}

Calculating
和上一题差不多,就是要减去 [ 1 , l ] [1,l] [1,l]的。

#include <iostream>
#include <cstring>
#include <cmath>
#include <bitset>
#include <queue>
#include <vector>
#include <cstdio>
#include <set>
#include <sstream>
#include <cstring>
#include <algorithm>
#include <map>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define reps(i, a, b) for (int i = a; i >= b; i--)
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a*b/gcd(a,b)
#define ls(node) tree[node].ls
#define rs(node) tree[node].rs
#define val(node) tree[node].val
using namespace std;
const int N = 1e6+ 7;
const int M = 50+7;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
typedef long long ll;
int main()
{
    ll n,m;
    scanf("%lld%lld",&n,&m);
    ll ans1=0,ans2=0;
    for(ll l=1,r;l<n;l=r+1){
        r=(n-1)/((n-1)/l);
        ans1+=(r-l+1+mod)%mod*((n-1)/l);
        ans1%=mod;
    }
    for(ll l=1,r;l<=m;l=r+1){
        r=m/(m/l);
        ans2+=(r-l+1+mod)%mod*(m/l);
        ans2%=mod;
    }
    printf("%lld\n",(ans2-ans1+mod)%mod);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值