数学,位运算,典型题

数学,自然想到组合数,逆元,阶乘

先来一发组合数相关,

#define MAXN 200001
const int mod=1000000007;

typedef long long ll;
int n,m,r,c;
ll ans,s;
ll inv[MAXN],fac[MAXN],dev[MAXN];
//inv[]逆元,fac[]阶乘,dev[]阶乘的逆元 

void chuli(int x) {
    inv[1]=1;
    fac[1]=1;
    dev[1]=1;
    fac[0]=1;
    dev[0]=1;
    for (int i=2;i<=x;i++) {
        fac[i]=fac[i-1]*i%mod;
        inv[i]=inv[mod%i]*(mod-mod/i)%mod;
        dev[i]=dev[i-1]*inv[i]%mod;

    }

}

ll C(int n,int m) {
    // C (m)
    //   (n)
    ll x=fac[n]*dev[n-m]%mod*dev[m]%mod;
    return x;
}

这是最普通的了,

然后是一些典型题,



No.1

i=1j=i n  Ai and Ai+1~~Aj

这道题要逐个位数去统计贡献

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
int W,n,line[100002];
ll ans;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&line[i]);
        while(line[i]>>W)W++;
    }

    for(int w=0;w<W;w++)
        for(int i=1,j=1;i<=n;i=++j)
            if((line[i]>>w)&1){
                while(j<=n&&(line[j]>>w)&1)j++;
                ans+=((ll)j-(ll)i)*(j-i+1)/2*(1<<w);
            }

    printf("%lld",ans);

    return 0;
}


然后还有对xor的优化

 1 ll num_xor(ll x) {
 2     int modans=x%4;
 3     if (modans==0) {
 4         return x;
 5     }
 6     if (modans==1) {
 7         return 1;
 8     }
 9     if (modans==2) {
10         return x+1;
11     }
12     return 0;
13 }
14
15 ll l,r;
16 scanf("%lld%lld",&l,&r);
17 ans=num_xor(r)^num_xor(l-1);
18 printf("%lld\n",ans);

这个只是 l xor l+1 xor l+2 ~~ xor r

连续xor的结果



还有错排公式

来吧

错排问题,是组合数学中的问题之一。考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)。研究一个排列错排个数的问题,叫做错排问题或称为更列问题。

D(n)=(n-1)[D(n-1)+D(n-2)];   D(1)=0; D(2)=1。

错排也有例题

比如这个

求有多少种长度为 n 的序列 A,满足以下条件:

1 ~ n 这 n 个数在序列中各出现了一次。

若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的。

满足条件的序列可能很多,序列数对 109+7 取模。

#include <cstdio>
#include <iostream>
#include <string>
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=1e6;
const ll mod=1e9+7;

ll n,m,ans;
ll f[N+5];
ll mul[N+5];
int T;

void chuli() {
    //错排预处理
    f[0]=1;
    f[1]=0;
    for (int i=2;i<=N;i++) {
        f[i]=((i-1)*(f[i-1]+f[i-2])%mod)%mod;
    }
    //阶乘预处理
    mul[0]=1;
    for (int i=1;i<=N;i++) {
        mul[i]=mul[i-1]*i%mod;
    }
}

//30~~43 求逆元
ll fast_pow(ll a,ll p) {
    ll ans=1;
    for (;p;p>>=1,a=a*a%mod) {
        if (p&1) {
            ans=ans*a%mod;
        }
    }
    return ans;
}
ll inv(ll x) {
    return fast_pow(x,mod-2);
}

ll get_C(ll n,ll m) {
    ll x=mul[n]%mod;
    ll y=mul[m]*mul[n-m]%mod;
    ll ans=x*inv(y)%mod;
    return ans;
}

int main () {
    chuli();
    scanf("%d",&T);
    while (T--) {
        scanf("%lld%lld",&n,&m);
        ans=get_C(n,m)*f[n-m]%mod;
        printf("%lld\n",ans);
    }

    return 0;
}


位运算还有典型题

No.2

所以对于一个序列,求出它们所有的连续和来说,小明觉得十分的简单。但今天小明遇到了一个序列和的难题,这个题目不仅要求你快速的求出所有的连续和,还要快速的求出这些连续和的异或值。小明很快的就求出了所有的连续和,但是小明要考考你,在不告诉连续和的情况下,让你快速求是序列所有连续和的异或值。

解释:

一般这种异或都是按位一位一位做的

对于某第k位,如果为1,那么说明

所有连续和异或的这第k位为1

如果满足这第k位为1的(s[i]-s[j])有cnt个

如果cnt为奇数,那么说明答案的第k位也等于1

如何求出第k位为1的(i,j)对数?

如果sum[i]第k位为1:

为了使第k位为1,要么sum[j]第k位为0且sum[j]前k-1位小于sum[i]前k-1位的大小

原因是如果红色条件不成立,进位后就变成了0

还有就是sum[j]第k位为1且sum[j]前k-1位大于sum[i]前k-1位的大小

同理,也是进位的问题

那么红色部分要求满足大小关系的对数,用两个树状数组就行

第k位为0同理

还可以这么解释:

一般这种位运算的题都要把每一位拆开来看,因为位运算每个位的结果这和这一位的数有关。

这样我们用s[i]表示a的前缀和,即a[1]+a[2]+....a[i],然后我们从这些数二进制最右位(2^0)开始,按照每一位对答案的贡献来计算。

假设我们现在算到最右位(2^0),并且位于第i个数,我们想要知道以i结尾的连续和对答案的贡献,只需要知道有多少s[i]-s[j](0<=j<i)的2^0位是1。 (设s[0]=0)

如果这个数是奇数,就说明异或了1奇数次,也就相当于异或了1,我们只需要把记录这一位总的异或贡献的变量cnt异或1即可;

如果是偶数就不用管了,对答案没有贡献。

对于数的每一位如果最后cnt=1的话,就说明在这一位所有连续和的异或和为1,我们就需要把答案加上(1<<(这个位数))。

那如何快速计算有多少个s[i]-s[j]的二进制第k位是否为1呢??

答案是利用权值树状数组。

考虑到Σa 最大才有1000000,我们构造两棵权值树状数组,一棵记录当前位为1的,另一棵记录为0的。

如果当前扫描到的s[i]的二进制第k位为1,那么对这一位的答案有贡献的只有那些第k位为1且第k位向右的数比s[i]第k位向右的数大的或者第k位为0且第k位向右的数不比s[i]第k位向右的数大的。(可能有点拗口,,都怪我语文学的不好)

为什么呢?

因为如果第k位都为1的话,那么只有后面那些位的和大于s[i]的数,s[i]减去它之后第k位才能出现1(因为s[i]比它小的话需要向更高位借数,就和小学学的横式减法差不多),从而对答案作出贡献;

如果第k位为0的话,如果后面再比s[i]大的话,s[i]第k位的1就需要借给低一位的了,所以后面必须不比s[i]大。

就是这样

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <string>
 4 #include <bits/stdc++.h>
 5
 6 using namespace std;
 7 #define MAXN 1000001
 8 int c[MAXN][2];
 9 int s[MAXN],a[MAXN],ans;
10 int pw[21],n;
11
12 void add(int x,int y) {
13     while (x<=1000000) {
14         c[x][y]++;
15         x+=(x&(-x));
16     }
17 }
18 int query(int x,int y) {
19     int sum=0;
20     while (x) {
21         sum+=c[x][y];
22         x-=(x&(-x));
23     }
24     return sum;
25 }
26 int main() {
27     int flag,cnt;
28     cin>>n;
29     for (int i=1; i<=n; i++) {
30         scanf("%d",&s[i]);
31         s[i]+=s[i-1];
32     }
33     pw[0]=1;
34     for (int i=1; i<=20; i++)
35         pw[i]=pw[i-1]*2;
36     for (int i=0; i<=20; i++) {
37         if (pw[i]<=s[n]) {
38             memset(c,0,sizeof(c));
39             flag=0;
40             add(1,0);
41             for (int j=1; j<=n; j++) {
42                 int tmp=s[j]&pw[i];
43                 if (tmp) {
44                     cnt=query(a[j]+1,0)+query(1000001,1)-query(a[j]+1,1);
45                 }
46                 else  {
47                     cnt=query(a[j]+1,1)+query(1000001,0)-query(a[j]+1,0);
48                 }
49                 if (cnt%2==1) flag^=1;
50                 add(a[j]+1,(bool)tmp);
51                 if (tmp)  {
52                     a[j]|=pw[i];
53                 }
54             }
55             if (flag) {
56                 ans|=(pw[i]);
57             }
58         }
59     }
60     cout<<ans;
61     return 0;
62 }

最后还要有小球与盒子的问题

           

           

原文地址:https://www.cnblogs.com/codemaker-li/p/9784741.html

时间: 2024-10-07 14:20:53

数学,位运算,典型题的相关文章

有关位运算的题

位运算 与& 0&0=0 1&0=0 0&1=0 1&1=1 或| 0|0=0 1|0=1 0|1=1 1|1=1 异或^ 0^0=0 1^0=1 0^1=1 1^1=0 题目: 实现一个函数,输入一个整数,输出该数的二进制表示中1的个数,例如9的二进制是1001,有两个1,输入9,输出1 程序1.0 写法1:模除法 int CountOne(int n) { int count = 0; while (n) { int ret = 0; ret = n % 2;

[位运算]签到题

题目描述 作为一道签到题,自然只能包含最基本的算法.本题的任务很简单,给定一个长度为n的序列a,你要将其排序.由于出题人很菜,不会排序算法,他决定自己编一个.他想找到一个数x,使得序列中的所有数字都异或上x后序列恰好按从小到大排列.顺带,这个序列会被进行若干次修改,每次修改后你需要回答当前是否存在一个x满足序列中数字异或上x后按从小到大排列,如果有,请你给出最小的x. 输入 第一行一个正整数n.第二行n个非负整数,表示序列a.第三行一个非负整数q,表示修改次数.接下来q行,每行一个正整数x和一个

461. Hamming Distance【数学|位运算】

2017/3/14 15:23:55 The Hamming distance between two integers is the number of positions at which the corresponding bits are different. Given two integers x and y, calculate the Hamming distance. 题目要求:求两个数字二进制位中同一位置不同bit的个数. 解法1  Java    利用1的移位依次匹配是否对

一道位运算技巧题

今天Mayuyu遇到一个非常有意思的题目,描述如下 题目:有一堆数,若干个数出现了3次,只有一个数出现了1次,找出只出现1次的那个数. 分析:把这个问题进行推广,得到如下描述 有一堆数,除了一个元素出现次外,其它每个数都出现了次,现在需要找出出现次的这个数. 代码: #include <bits/stdc++.h> using namespace std; const int N = 10005; int a[N]; int singleNumber(int a[], int n, int p

LeetCode刷题总结-双指针、位运算和分治法篇

本文总结LeetCode上有关双指针.位运算和分治法的算法题,推荐刷题总数14道.具体考点分析如下图: 一.双指针 1.字符串和数组问题 题号:424. 替换后的最长重复字符,难度中等 题号:828. 独特字符串,难度困难 题号:923. 三数之和的多种可能,难度中等 2.实际场景应用问题 题号:826. 安排工作以达到最大收益,难度中等 3.元素对问题 题号:986. 区间列表的交集,难度中等 二.位运算 1.字符串和数组问题 题号:137. 只出现一次的数字 II,难度中等 题号:318.

位运算和典型应用详解

位运算的符号 与运算:& 或运算:| 异或运算:^ 非运算:~ 移位运算:>>和<< 一. 逻辑运算符1. & 位与运算 1) 运算规则 位与运算的实质是将参与运算的两个数据,按对应的二进制数逐位进行逻辑与运算.例如:int型常量4和7进行位与运算的运算过程如下:4=0000 0000 0000 0100 &7 =0000 0000 0000 0111= 0000 0000 0000 0100对于负数,按其补码进行运算.例如:例如:int型常量-4和7进行位

关于位运算的水题

找数字2 Time Limit: 25000ms, Special Time Limit:50000ms, Memory Limit:32768KB Total submit users: 92, Accepted users: 67 Problem 11466 : No special judgement Problem description 给定2n+1个数字,只有一个数字出现了奇数次,其余的数字都出现了偶数次,现在你需要找出出现奇数次的数字. Input 包含多组数据,每组数据第一行为一

bzoj5108 [CodePlus2017]可做题 位运算dp+离散

[CodePlus2017]可做题 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 87  Solved: 63[Submit][Status][Discuss] Description qmqmqm希望给sublinekelzrip出一道可做题.于是他想到了这么一道题目:给一个长度为n的非负整数序列ai,你需 要计算其异或前缀和bi,满足条件b1=a1,bi=bi?1 xor ai(i≥2).但是由于数据生成器出现了问题,他生成的序列a 的长度特

HDU1196_Lowest Bit【位运算】【水题】

Lowest Bit Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 8043    Accepted Submission(s): 5920 Problem Description Given an positive integer A (1 <= A <= 100), output the lowest bit of A. For