JZOJ2017.08.10 B组

59 篇文章 0 订阅

T1

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)。

思路:

欧拉+筛素数
首先,令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\n",answer);
    }
    return 0;
}

T2

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

Input

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

Output

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

思路:

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

代码:

var  f:array[0..1001,0..1001]of boolean;
     ans,n,m,all,i,j,x,max,maxn,maxm:longint;
     h,l:array[0..1001]of longint;
procedure init;
begin
  ans:=100000000;
  read(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;
end;
procedure doit;
begin
   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;
end;
procedure dfs(temp:longint);
var i: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;
        doit;
        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

  init;
  dfs(0);
  write(ans);

end.

T3

Description

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

Input

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

Output

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

思路:

DP
设f[i][j]为i个盘子j根柱子的最小移动步数
状态转移方程为2*f[k][j]+f[i-k][j-1]
(其实这个方程有点像分治)意思就是,将一个几个盘子分成两份

代码:

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 init;
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;
end;
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;
  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
  init;
  dfs(0);
  writeln(ans);
end.
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值