zoukankan      html  css  js  c++  java
  • JZOJ 8.14 B组总结

    NO.1

    Description

    我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数K,你需要回答他最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可。

    Input

    输入一行,包含4个空格分开的正整数,依次为N,K,L和H。

    Output

    输出一个整数,为所求方案数。

    Sample Input

    2 2 2 4
    Sample Output

    3
    HINT

    样例解释

    所有可能的选择方案:(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)

    其中最大公约数等于2的只有3组:(2, 2), (2, 4), (4, 2)

    对于100%的数据,1≤N,K≤10^9,1≤L≤H≤10^9,H-L≤10^5


    思路:容斥原理+快速幂
    设g[i]表示以k*i为最小公约数的选数方案
    那么对于当前的g[i]怎么求呢?
    首先对于每一个i记一个left表示a/(k*i),right表示b/(k*i)
    其实不算最大公约数大于k*i的情况下,就等于(right-left+1)^n-(right-left+1),其实这个题目上也有说明。。。用快速幂求即可
    那么当前g[k*i]的真正答案就等于g[i]-g[i*2]-g[i*3]……g[k*i] (k*i<=maxn)


    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int mo=1000000007;
    const int maxn=100000;
    int n,k,a,b,l,r,g[200020];
    int ksm(int x,int k)
    {
        int r=1;
        while (k)
        {
            if (k&1) r=(1LL*r*x)% mo;
            k>>=1;
            x=(1LL*x*x)% mo;
        }
        return r;
    }
    int main()
    {
        freopen("number.in","r",stdin);
        freopen("number.out","w",stdout);
        scanf("%d%d%d%d",&n,&k,&a,&b);
        l=a/k; r=b/k; if (a%k) l++;
        for (int i=maxn;i>=1;i--)
        {
            int left=l/i,right=r/i;
            if (l%i) left++;
            if (l<=r)
            {
                g[i]=ksm(right-left+1,n);
                g[i]=(g[i]-(right-left+1)+mo)%mo;
                for (int j=i*2;j<=maxn;j+=i) g[i]=(g[i]-g[j]+mo)% mo;
            }
        }
        if (l==1) g[1]=(g[1]+1)%mo;
        printf("%d",g[1]);
        return 0;
    }

    NO.2

    Description

      小H是个数学天才,在空闲的时候他喜欢发明各种各样的函数来自娱自乐。再过不久就是小H的同学,小小H的生日了。小H决定将自己刚刚发明的第19911026号函数当作生日礼物送给小小H,并把它命名为H函数。   
      其中H函数是这样定义的:H[A,B,C,D(x)] = (A * B^x + C) mod D。 在生日party上,小H将自己精心构想的礼物送上。同样精通数学的小小H对H函数自然非常感兴趣,以至于把其他礼物冷落一旁,专心研究H函数。 她想 如果给定一个正整数m,那么可以用F(A, B, C, D) 表示 H[A,B,C,D(x)]在 [1,m]取最小值时的x(x为正整数,且数据保证x值唯一) 善于思考的小小H很快想到一个问题来考考参加party的朋友们。   即给定n, m, {An}, {Bn}, {Cn}, {Dn},求出所有的F(Ai, Bi, Ci, Di)。   
      追求完美的小小H觉得答案念起来不够好听,为了能达到朗朗上口的效果,她偷偷修改了数据来满足F(Ai, Bi, Ci, Di) ≤ F(Ai+1, Bi+1, Ci+1, Di+1)。

    Input

      第1行包含2个正整数n, m。   
      第2 ~ n + 1行每行包含4个非负整数Ai, Bi, Ci, Di。

    Output

      输出文件包含n行,第i + 1行为一个整数,表示F(Ai, Bi, Ci, Di)。   
      保证F(Ai, Bi, Ci, Di) ≤ F(Ai+1, Bi+1, Ci+1, Di+1)。

    Sample Input

    3 5
    373911025 1525760443 652804587 1767005941
    120055457 159868670 59429374 196292251
    1200581 955324 141748 2705431

    Sample Output

    1
    2
    4

    Hint

    温馨提示:在F(Ai+1,Bi+1,Ci+1,Di+1)中,(i+1)为下标 30%的数据满足:1 ≤ n, m ≤ 1000 60%的数据满足:1 ≤ n, m ≤ 10000 100%的数据满足: 1 ≤ n, m ≤ 100000,0

    #include<cstdio>
    #include<iostream>
    using namespace std;
    long long n,m,a[100001],b[100001],c[100001],d[100001],ans[100001];
    long long ksm(long x,long y,long long z)
    {
        long long r=x,k=1;
        while (y>0)
        {
            if (y&1==1) k=(k*r)%z;
            y=y>>1;
            r=(r*r)%z;
        }
        return k;
    }
    void doit(long long l,long long r,long head,long long tail)
    {
        if (l>r) return;
        long long mid,min1,x,y,w;
        w=0;
        y=head;
        mid=(l+r)/2;
        x=ksm(b[mid],head-1,d[mid]);
        min1=d[mid];
        for (long long i=head;i<=tail;i++) 
        {
            w=(((((a[mid]*x)%d[mid])*b[mid])%d[mid])+c[mid])%d[mid];
            if (w<min1)
            {
                min1=w;
                y=i;
            }
            x=(x*b[mid])%d[mid];
        }
        ans[mid]=y;
        doit(l,mid-1,head,y);
        doit(mid+1,r,y,tail);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (long long i=1;i<=n;i++) scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
        doit(1,n,1,m);
        for (long long i=1;i<=n;i++) printf("%d
    ",ans[i]);
    }

    NO.3

    Description

    Alice总是会提出很多奇怪的问题,一天他让他的朋友Bob跟他一起研究一个奇怪的问题。问题是:[A,B]中有多少个数满足组成这个数的数字之和为S,另一个问题是[A,B]内满足这一要求最小的数是哪个?
    编程帮Bob解决这个问题。

    Input

    输入包含三个整数A,B和S(1<=A<=B<10^15,1<=S<=135)。

    Output

    第一行输出一个整数表示满足条件的数的个数;
    第二行输出最小的满足条件的数。
    输入文件保证至少存在一个数。

    Sample Input

    输入1:
    1 9 5

    输入2:
    1 100 10

    输入3:
    11111 99999 24

    Sample Output

    输出1:
    1
    5

    输出2:
    9
    19

    输出3:
    5445
    11499


    思路:数位dp
    设f[i][j][0/1][0/1]表示前i位,和为j,第一个0/1表示是否等于(0)或大于a(1),第二个0/1表示是否等于(0)或小于b(1)
    四重循环枚举
    对于每一个k和l,我们可以得出下一位枚举的范围
    对于新加后的数,求出相应的k和l,进行转移
    这样第一个问题就解决了
    那么求最小值,我们还可以定义一个与上面差不多的方程g[i][j][0/1][0/1]
    这样可以用同样的时间做两件事
    最后的方案数=f[len+1][s][0][0]+f[len+1][s][0][1]+f[len+1][s][1][0]+f[len+1][s][1][1]
    最小值=min(min(g[len+1][s][0][0],g[len+1][s][0][1]),min(g[len+1][s][1][0],g[len+1][s][1][1]))


    代码:

    uses math;
    var a,b:int64;
        i,j,k,l,s,z,len,l1,l2,k2,r1:longint;
        x,y:array[0..16]of integer;
        f,g:array[0..16,0..136,0..1,0..1]of int64;
    
    procedure init;
    var i:longint;
        s1:string;
    begin
      readln(a,b,s);
      str(b,s1);
      len:=length(s1);
      for i:=1 to len do y[i]:=ord(s1[i])-48;
      str(a,s1);
      while (length(s1)<len) do s1:='0'+s1;
      for i:=1 to len do x[i]:=ord(s1[i])-48;
      fillchar(g,sizeof(g),127);
      f[1,0,0,0]:=1;
      g[1,0,0,0]:=0;
    end;
    
    begin
      init;
      for i:=1 to len do
        for j:=0 to s do
          for k:=0 to 1 do
            for l:=0 to 1 do
              if f[i,j,k,l]>0 then
                begin
                  if k=0 then l1:=x[i] else l1:=0;
                  if l=0 then r1:=y[i] else r1:=9;
                  for z:=l1 to r1 do
                    if (j+z<=s) then
                      begin
                        if z>x[i] then k2:=1 else k2:=k;
                        if z<y[i] then l2:=1 else l2:=l;
                        inc(f[i+1,j+z,k2,l2],f[i,j,k,l]);
                        if (g[i,j,k,l]*10+z)<g[i+1,j+z,k2,l2] then g[i+1,j+z,k2,l2]:=g[i,j,k,l]*10+z;
                      end;
                end;
      writeln(f[len+1,s,0,0]+f[len+1,s,0,1]+f[len+1,s,1,0]+f[len+1,s,1,1]);
      writeln(min(min(min(g[len+1,s,0,0],g[len+1,s,0,1]),g[len+1,s,1,0]),g[len+1,s,1,1]));
    end.
    

    NO.4

    Description

      Alice又想到一个游戏:N个数每个数都在0到9之间,可以对每一个数进行加1操作,但这个加1比较特别,0-8加1后会相应变成1-9,但9加1后会变成0,给出N个数,进行M次操作,每次操作都会给出两个整数A和B(1<=A<=B<=N),要求输出第A个数到第B个数的和,然后把A到B之间的每一个数进行一次加1操作。
      你能帮助Alice吗?

    Input

      输入文件第一行包含两个整数N和M(1<=N<=250000,1<=M<=100000);
      第二行N个数字,每个数字都在0到9之间,中间没有空格,表示初始值。
      接下来M行,每行包含两个整数A和B(1<=A<=B<=N)

    Output

      输出M行,每行表示对应区间的和,注意是先计算后操作。

    Sample Input

    输入1:
    4 3
    1234
    1 4
    1 4
    1 4

    输入2:
    4 4
    1234
    1 1
    1 2
    1 3
    1 4

    输入3:
    7 5
    9081337
    1 3
    3 7
    1 3
    3 7
    1 3

    Sample Output

    输出1:
    10
    14
    18

    输出2:
    1
    4
    9
    16

    输出3:
    17
    23
    1
    19
    5


    思路:线段树
    我们可以用tree[i][j]记录节点i拥有j数量的值
    每次直接查询
    再码个区间修改就好了


    代码:

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #define N 250010
    using namespace std;
    char c[N];
    int n,m;
    int t[N<<2],tree[N<<2][10];
    void ps(int x)
    {
        for(int i=0;i<10;++i) tree[x][i]=tree[x<<1][i]+tree[x<<1|1][i];
    }
    int sum(int x)
    {
        int ans=0;
        for(int i=1;i<10;++i) ans+=i*tree[x][i];
        return ans;
    }
    void rev(int* x,int k)
    {
        if(k>10) k%=10; 
        static int v[10];
        memcpy(v,x+(10-k),k<<2);
        memmove(x+k,x,(10-k)<<2);
        memcpy(x,v,k<<2);
    }
    void pd(int m,int x)
    {
        if(t[x])
        {
            t[x<<1]+=t[x];
            t[x<<1|1]+=t[x];
            rev(tree[x<<1],t[x]);
            rev(tree[x<<1|1],t[x]);
            t[x]=0;
        }
    }
    void build(int l,int r,int x)
    {
        if(l==r){ tree[x][c[l]-'0']++; return; }
        int mid=l+r>>1;
        build(l,mid,x<<1);
        build(mid+1,r,x<<1|1);
        ps(x);
    }
    void plu(int l,int r,int x,int head,int tail)
    {
        if(head<=l&&r<=tail)
            { 
            t[x]++; rev(tree[x],1); return; 
        }
        int mid=l+r>>1; pd(r-l+1,x);
        if(head<=mid) plu(l,mid,x<<1,head,tail);
        if(mid<tail) plu(mid+1,r,x<<1|1,head,tail);
        ps(x);
    }
    int query(int l,int r,int x,int head,int tail)
    {
        if(head<=l&&r<=tail) return sum(x);
        int mid=l+r>>1,ans=0; 
            pd(r-l+1,x);
        if(head<=mid) ans+=query(l,mid,x<<1,head,tail);
        if(mid<tail) ans+=query(mid+1,r,x<<1|1,head,tail);
        return ans;
    }
    int main()
    {
        scanf("%d%d%s",&n,&m,c+1);
        build(1,n,1);
        for(int a,b,i=0;i<m;++i)
        {
            scanf("%d%d",&a,&b);
            printf("%d
    ",query(1,n,1,a,b));
            plu(1,n,1,a,b); 
        }
    }
  • 相关阅读:
    TCP流量控制,拥塞控制原理
    Java数组--求一个数组中连续m个数的和最大的数组组合
    一次使用IDEA编写JDK动态代理Class数组中有关泛型的问题
    Java数组--一个整型数组,给定一个定数,求数组中两个数的和与定数相等
    Java基础知识--Stream接口的理解与应用
    JSAP107
    JSAP106
    JSAP105
    JSAP104
    JSAP103
  • 原文地址:https://www.cnblogs.com/Comfortable/p/8412257.html
Copyright © 2011-2022 走看看