• JZOJ 8.10 B组总结


    NO.1

    Description

    对于两个整数k 和m,如果k 和m 的最大公约数为1,则k 和m 互质。给出两个正整
    数n 和m(m≤n),定义f(n,m)为1~n!中与m!互质的数的个数。其中n!=1*2*3*..*(n-1)*n。
    Task:给定n 和m,要求计算f(n,m)。

    Input

    本题设多组数据。
    输入文件的第一行有一个整数T(1≤T≤100000),表示有T 组数据。
    接下来有T 行,每行两个整数n 和m(2≤n≤100000,2≤m≤n)。

    Output

    输出文件包含T 行,每行一个整数,表示f(n,m)。
    由于答案过大,所以你只要输出f(n,m) mod 131071。
    131071 是M17(梅森素数,2^17-1)。

    Sample Input

    1
    3 2

    Sample Output

    3

    数据约定:
    对于50%的数据,T=1,2≤N≤10
    对于80%的数据,T=1,2≤N≤100000
    对于100%的数据,1≤T≤100000,2≤N≤100000
    AcFast 友情提示:小心运算溢出,也就是RTE215 错误。。。


    思路:欧拉+筛素数
    首先,令n=N!,m=M!
    ∵M<=N
    ∴m|n,所以问题化为求1-n中与m互质的数的个数,即(n/m)*phi(m),其正确性是显然的。
    如果一个数x< m且和m互质,即gcd(x,m)=1,那么gcd(x+km,m)=1,假设x+km和m不互质,那么一定存在一个m的因数y,使得y也是x+km的因数,即能写成y(x/y+km/y),但是x中没有y这个因子,
    ∴假设不成立
    ∴gcd(x+km,m)=1,对于每一个小于m的和m互质的数,都能用这个式子退出剩下的大于m小于等于n且和m互质的数,于是我们得到ans=(n/m)phi(m),这样是否包含了所有的答案呢?
    可以知道对于任意一个与m互质的数,减去km一定能得到m以内的一个和m互质的数,因此上式包含了所有的符合要求的数
    由于M!是阶乘,所以phi(i)=M!(1-1/p1)(1-1/p2)…(1-1/pk),1到pk是小于等于M的所有素数,O(MloglogM)筛出,约去M!得ans=N!(1-1/p1)(1-1/p2)…(1-1/pk),所有的N!都可以在O(N)的预处理求出,因为要modR,所以需要求p1到pk的逆元,方程inv[i]=(M-M/i)* inv[M%i]%M,O(N)的预处理。
    我们观察式子:ans=N!(1-p1/1)(1-p2/1)…(1-pk/1),对于不同的询问,只有N!和k不同,既然能够预处理N!,何不预处理后面的乘积?进行O(M)的预处理,每次回答的复杂度降为O(1)。


    代码如下:

    #include <cstdio>
    #include <bitset>
    using namespace std;
    const int N=100005;
    const int mod=131071;
    bitset<N> zs;
    long long jc[N],ans[N],times[N];
    void ycl()
    {
        int i,j;
        zs.set();
        for (i=2;i<N;i++) if (zs[i]) for (j=i+i;j<N;j+=i) zs[j]=false;
        jc[0]=1;     
        for (i=1;i<N;i++) jc[i]=jc[i-1]*i %mod;
        times[1]=1;   
        for (i=2;i<N;i++)
        {
            if(i>=mod) break;
            times[i]=(mod-mod/i)*times[mod%i]%mod;
        }
        ans[1]=1;
        for (i=2;i<N;i++)
            if (zs[i])
            {
                ans[i]=ans[i-1]*(i-1)% mod;
                ans[i]=ans[i]*times[i%mod]% mod;
            }
            else ans[i]=ans[i-1];
    }
    
    int main ()
    {
        int T;
        scanf("%d",&T);
        ycl();
        int m,n;
        while (T--)
        {
              scanf("%d%d",&n,&m);
              long long answer=jc[n]*ans[m]%mod;
              printf("%lld
    ",answer);
        }
        return 0;
    }

    NO.2

    Description

    给你一个N*M 的矩阵,矩阵里面的元素要么是正数,要么是负数,它们的绝对值不大
    于10000。现在你可以对矩阵进行两种操作:
    1、将某一列的元素全部取反。
    2、将某一行的元素全部取反。
    你可以执行任意次操作。
    Task:通过以上两种操作如果可以将整个矩阵的元素全变为正数的话,则输出最少的操
    作次数,否则输出“impossible”(不包括双引号)。

    Input

    输入文件的第一行有两个整数n 和m(1≤n,m≤1000),表示矩阵的大小。
    接下来有N 行,每行M 个数,每个数之间有一个空格。

    Output

    通过以上两种操作如果可以将整个矩阵的元素全变为正数的话,则输出最少的操作次
    数,否则输出“impossible”(不包括双引号)。

    Sample Input

    2 4
    3 7 -6 1
    -2 -6 8 -4

    Sample Output

    2

    Hint

    数据约定:
    对于40%的数据,1≤N,M≤10
    对于100%的数据,2≤N,M≤1000


    思路:贪心+dfs
    首先,我们可以预处理出每一行的负数个数和每一个位置的正负
    那么我们在每次dfs里,求出最多负数的行或列(一个)
    将这行或列取反,算出取反后的负数个数(如果说是行,会影响到当前列上的值;如果是列,会影响到当前行上的值)和每一个位置的正负
    如果每次都判断一遍整个矩阵有没有负数,绝逼会TLE,那么就可以预处理出一个矩阵的负数个数,每次取反便改变
    如果在800步内没有做出,输出impossible


    代码:

    var  f:array[0..1001,0..1001]of boolean;
         ans,n,m,all,i,j,x:longint;
         h,l:array[0..1001]of longint;
    
    procedure dfs(temp:longint);
    var i,max,maxn,maxm:longint;
    begin
            if (all=0) then
            begin
                   if (temp<ans) then ans:=temp;
                   exit;
            end;
            if (temp>800) then
            begin
                    write('impossible');
                    halt;
            end;
            max:=0;maxn:=0;maxm:=0;
            for i:=1 to n do if (max<h[i])then  begin max:=h[i]; maxn:=i; maxm:=1; end;
            for i:=1 to m do if (max<l[i])then  begin max:=l[i]; maxn:=i; maxm:=2; end;
            if (maxm=1) then
            begin
                     h[maxn]:=m-h[maxn];
                     for i:=1 to m do
                            if (f[maxn][i]) then
                            begin
                                         f[maxn][i]:=false;
                                         inc(l[i]);
                                         inc(all);
                     end
                     else
                     begin
                                         f[maxn][i]:=true;
                                         dec(l[i]);
                                         dec(all);
                     end;
                     dfs(temp+1);
                     h[maxn]:=m-h[maxn];
                     for i:=1 to m do
                            if (f[maxn][i]) then
                            begin
                                         f[maxn][i]:=false;
                                         inc(l[i]);
                                         inc(all);
                     end
                     else
                     begin
                                         f[maxn][i]:=true;
                                         dec(l[i]);
                                         dec(all);
                     end;
            end
            else
            begin
                     l[maxn]:=n-l[maxn];
                     for i:=1 to n do
    
                            if (f[i][maxn]) then
                            begin
                                         f[i][maxn]:=false;
                                         inc(h[i]);
                                         inc(all);
                     end
                     else
                     begin
                                         f[i][maxn]:=true;
                                         dec(h[i]);
                                         dec(all);
                     end;
                     dfs(temp+1);
                     l[maxn]:=n-l[maxn];
                     for i:=1 to n do
    
                            if (f[i][maxn])then
                            begin
                                         f[i][maxn]:=false;
                                         inc(h[i]);
                                         inc(all);
                     end
                     else
                     begin
                                         f[i][maxn]:=true;
                                         dec(h[i]);
                                         dec(all);
                     end;
            end;
    
    end;
    
    begin
      ans:=100000000;
      readln(n,m);
      for i:=1 to n do
      begin
            for j:=1 to m do
            begin
              read(x);
              if x<0 then
              begin
                    f[i][j]:=false;
                    inc(h[i]); inc(l[j]);
                    inc(all);
              end
              else f[i][j]:=true;
            end;
      end;
      dfs(0);
      writeln(ans);
    end.
    

    NO.3

    Description

    现在给你M 根柱子,初始的时候有N 个大小不一样的盘插在第一根柱子上面。同
    样地,规格大的盘子不能放在规格比它小的盘子上面。问最少需要多少次的移动才能将
    这N 个盘从第一根柱子移动到最后一根柱子上面?

    Input

    输入文件的第一行有两个整数n,m(1≤n≤100000,3≤m≤10),分别表示有n 个盘子和m
    根柱子。

    Output

    输出文件只有一行,一个整数,表示最少的移动次数。保证这个移动次数不会超过
    2^63-1。

    Sample Input

    4 3

    Sample Output

    15

    数据约定:
    对于30%的数据,M=3
    对于80%的数据,1≤N≤100,3≤M≤10
    对于100%的数据,1≤N≤100000,6≤M≤10


    思路:DP
    设f[i][j]为i个盘子j根柱子的最小移动步数
    状态转移方程为2*f[k][j]+f[i-k][j-1]
    (其实这个方程有点像分治)意思就是,将一个几个盘子分成两份,如下图
    这里写图片描述
    那么这个决策点k怎么去枚举呢,假设g[i]表示有i个盘子时的决策点,g[i]=g[i-1]
    如果当前决策点不够它的+1优,就+1


    代码:

    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    unsigned long long f[100010][12];
    int g[100010];
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for (int j=3;j<=m;j++)
        {
            g[0]=0;
            for (int i=1;i<=n;i++)
            {
                if (j==3) {f[i][j]=f[i-1][j]*2+1; continue;}
                g[i]=g[i-1];
                if (i<j) f[i][j]=i*2-1; else
                {
                    int k=g[i];     
                    f[i][j]=2*f[k][j]+f[i-k][j-1];
                    if (f[k+1][j]*2+f[i-k-1][j-1]<f[i][j]) f[i][j]=f[k+1][j]*2+f[i-k-1][j-1],g[i]++;
                }
            }
        }
        printf("%lld",f[n][m]);
    }

    NO.4

    Description

    Z 国是一个拥有N 个岛的国家。这N 个岛用N-1 条桥来连接,且任意两个岛之间都可以互达。
    某商人听说Z 国是一个很富有的国家,所以他想到Z 国闯一闯。经过他仔细的观察,他发现某样商品特别受欢迎,而且由于各岛之间沟通联系不够多,所以这样物品在每个岛的价格可能都不同。
    Task:商人开始在编号为x 的岛上,然后他要走到编号为y 的岛上。在这期间,他可以在x 岛y 的路径上买一件商品,和卖一件商品。,注意,仅能买一件和卖一件!显然你要计算商人从岛x 到岛y 最多能赚多少钱。

    Input

    输入第一行有一个整数N(1≤n≤50000),表示Z 国有N 个岛。
    接下来有N 行,每行一个整数Ci(1≤Ci≤50000),第N+i 行的Ci 表示商品在岛i 的价
    格。
    再接下来有N-1 行,每行两个整数x,y(1≤x,y≤50000),表示岛x 和岛y 之间有一条
    桥。
    接下来有一个整数M,表示有M 个询问。
    然后M 行,每行两个整数,x,y(1≤x,y≤50000),表示询问你,商人从岛x 到岛y 最多
    能赚多少钱?

    Output

    对于每次询问,如果商人能赚到钱,则输出最多能赚多少钱。
    如果不能赚钱,就输出0(你可以这样理解——亏本生意谁都不会做^_^)

    Sample Input

    4
    1
    2
    3
    4
    1 2
    1 4
    2 3
    3
    1 3
    3 1
    1 4

    Sample Output

    2
    0
    3

    Hint

    数据约定:
    对于30%的数据, 1≤N,M≤100
    对于60%的数据,1≤N,M≤1000
    对于100%的数据,1≤N,M≤50000


    思路:Tarjan
    这里写图片描述


    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n,m,i,next[1000001],last[100001],bridge[100001],tot,a[50001],x[50001],y[50001],ans,k[50001][101][3],b[50001],way[50001];
    bool bz[50001];
    int find(int t)
    {
        if (b[t]==t) return t;
        else return find(b[t]);
    }
    void Tarjan(int l)
    {
        int i=last[l],yy=0;
        bz[l]=true;
        while (i)
        {
            yy=bridge[i];
            if (bz[yy]==false)
            {
                Tarjan(yy);
                b[yy]=l;
            }
            i=next[i];
        }
        for (i=1;i<=k[l][0][0];i++) if (bz[k[l][i][1]]) k[l][i][2]=find(k[l][i][1]);
    }
    void get(int l,int w)
    {
        if (l==w)
        {
              way[++way[0]]=l;
              return;
        }
        else 
        {
             way[++way[0]]=l;
             get(b[l],w);
             return;
        }
    }
    void get2(int l,int w)
    {
        while (l==w)
        {         
                  get(b[l],w);
                  way[++way[0]]=l;
                  return;
        }
    } 
    int main()
    {
        int x1,y1;
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            b[i]=i;
        }
        for (int i=1;i<=n-1;i++)
        {
            scanf("%d%d",&x1,&y1);
            tot++; bridge[tot]=y1; next[tot]=last[x1]; last[x1]=tot;
            tot++; bridge[tot]=x1; next[tot]=last[y1]; last[y1]=tot;
        }
        scanf("%d",&m);
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d",&x[i],&y[i]);
            k[x[i]][++k[x[i]][0][0]][1]=y[i];
            k[y[i]][++k[y[i]][0][0]][1]=x[i];
        }
        Tarjan(1);
        int w=0;
        for (int i=1;i<=m;i++)
        {
            for (int j=1;j<=k[x[i]][0][0];j++)
                if (k[x[i]][j][1]==y[i])
                {
                                          w=k[x[i]][j][2];
                                          break;
                }
            if (w==0)
            {
                     for (int j=1;j<=k[y[i]][0][0];j++)
                         if (k[y[i]][j][1]==x[i])
                         {
                                          w=k[y[i]][j][2];
                                          break;
                         }     
            }
            way[0]=0;
            get(x[i],w);get2(y[i],w);
            int mx[50001],mn[50001],ans=0;
            mn[0]=2147483647;mx[way[0]+1]=0;
            for (int j=1;j<=way[0];j++) mn[j]=min(mn[j-1],a[way[j]]);   
            for (int j=way[0];j>=1;j--) mx[j]=max(mx[j+1],a[way[j]]);   
            for (int j=2;j<=way[0];j++) ans=max(ans,mx[j]-mn[j-1]);
            printf("%d
    ",ans);
        }
    }
    
  • 相关阅读:
    CI框架(Codeigniter)总结
    ssh自动下载SFTP文件
    数据库设计原则
    Java代码性能优化的 39个细节
    quartz定时任务时间设置
    Myeclipse 反编译工具插件
    Maven -- 使用Myeclipse创建Maven项目
    详解Java Web项目启动执行顺序
    java web项目下的lib和build path 中jar包问题解惑
    java读取存在src目录下和存在同级目录下的配置文件
  • 原文地址:https://www.cnblogs.com/Comfortable/p/8412258.html
Copyright © 2020-2023  润新知