• (题目练习)条件概率+权值线段树+FWT+后缀数组


    Game
    意思就是给一个含有n个数的排列,每次取两个数(只剩一个就不取了),如果两个数互质加一分,求分数的期望值。

    期望的线性性
    E(x+y)=E(x)+E(y)
    主要作用于条件概率,比如有n个物品,其中有特定的物品m个,在x次抽取的过程中,抽取到一次特定物品就得到y分,求x次抽取得分的期望

    期望分数等于=(第一次抽取时抽到特定物品的得分期望)*x

    例如:
    52张牌,抽取10次,抽到‘A’得分为2,得分期望就是 1/13210

    #include 
    
    using namespace std;
    int gcd(int a,int b)
    {
        return b==0?a:gcd(b,a%b);
    }
    int main()
    {
        int n;
        cin>>n;
        int cnt=0;
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                if(gcd(i,j)==1)
                    cnt++;
        if(n&1)
        {
            int temp=gcd(n,cnt);
            temp=max(temp,1);
            cout<<cnt/temp<<"/"<<(n)/temp<<endl;
        }
        else
        {
            int temp=gcd(n-1,cnt);
            temp=max(temp,1);
            cout<<cnt/temp<<"/"<<(n-1)/temp<<endl;
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    P1637 三元上升子序列
    给一个数组求a[i]

    自然能想到的是方案数就是对于每个位置j来讲,我们令所有前方小于j位置的数*所有后方大于j位置的数

    那么问题的关键就在于如何确定每个位置的个数。这里用权值线段树来求,我们通过每次从左到右插入一个数之前,查询整个线段树里所有小于目前即将插入的这个数的个数,这样就能保证i

    #include
    #define ls rt<<1
    #define rs rt<<1|1
    #define mid ((l+r)>>1)
    #define int long long
    using namespace std;
    const int N = 1e7+100;
    struct node
    {
        int num,pos;
    };
    bool cmp(const node &a,const node &b)
    {
        return a.num<b.num;
    }
    node a[N];
    int num[N],cnt,tree[N<<2],small[N],big[N];
    int ans;
    void up(int rt)
    {
        tree[rt]=tree[ls]+tree[rs];
    }
    void fix(int rt,int l,int r,int num)
    {
        if(l==r&&l==num)
        {
            tree[rt]++;
            return;
        }
        if(num<=mid)
            fix(ls,l,mid,num);
        else
            fix(rs,mid+1,r,num);
        up(rt);
    }
    int query(int rt,int l,int r,int nl,int nr)
    {
        if(nl<=l&&r<=nr)
            return tree[rt];
         int res=0;
        if(nl<=mid)
            res+=query(ls,l,mid,nl,nr);
        if(nr>mid)
            res+=query(rs,mid+1,r,nl,nr);
        return res;
    }
    signed main()
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i].num;
            a[i].pos=i;
        }
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=n;i++)
        {
            if(a[i].num>a[i-1].num)
                cnt++;
            num[a[i].pos]=cnt;
        }
        for(int i=1;i<=n;i++)
        {
            if(num[i]>1)
            small[i]=query(1,1,n,1,num[i]-1);
            fix(1,1,n,num[i]);
        }
        memset(tree,0,sizeof(tree));
        for(int i=n;i>=1;i--)
        {
            if(num[i]<n)
            big[i]=query(1,1,n,num[i]+1,n);
            fix(1,1,n,num[i]);
        }
        for(int i=1;i<=n;i++)
        {
            ans+=small[i]*big[i];
        }
        cout<<ans<<endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    Circulant Matrix
    题目大意:

    给定a,b数组,和如下关系,求x数组
    The element A[i][j] satisfies A[i][j] = a[i xor j],
    A[0][0]*x[0] + A[0][1]*x[1] + A[0][2]*x[2] + A[0][3]*x[3] = b[0] (mod p)
    A[1][0]*x[0] + A[1][1]*x[1] + A[1][2]*x[2] + A[1][3]*x[3] = b[1] (mod p)
    A[2][0]*x[0] + A[2][1]*x[1] + A[2][2]*x[2] + A[2][3]*x[3] = b[2] (mod p)
    A[3][0]*x[0] + A[3][1]*x[1] + A[3][2]*x[2] + A[3][3]*x[3] = b[3] (mod p)

    显然能看到,A数组是由a数组对应异或下标位置得来,
    易得出x数组是b和1/a的卷积得来,利用fwt加速卷积过程即可。

    #include
    #include
    #include
    #include
    #include
    #define inf 1e18
    using namespace std;
    int n;
    const long long p = 1e9 + 7;
    const int maxn = 263000;
    long long a[maxn], b[maxn];
    
    long long qpow(long long a, long long b)
    {
    	long long ans = 1;
    	while (b) {
    		if (b & 1) {
    			ans = ans * a % p;
    		}
    		a = a * a % p;
    		b = b >> 1;
    	}
    	return ans;
    }
    
    void fwt(long long a[])
    {
    	for (int d = 1; d < n; d <<= 1) {
    		for (int m = d << 1, i = 0; i < n; i += m) {
    			for (int j = 0; j < d; j++) {
    				long long x = a[i + j], y = a[i + j + d];
    				a[i + j] = (x + y) % p;
    				a[i + j + d] = (x - y + p) % p;
    			}
    		}
    	}
    }
    
    void ifwt(long long a[])
    {
    	long long inv = qpow(2, p - 2);//并不知道为什么是p - 2
    	for (int d = 1; d < n; d <<= 1) {
    		for (int m = d << 1, i = 0; i < n; i += m) {
    			for (int j = 0; j < d; j++) {
    				long long x = a[i + j], y = a[i + j + d];
    				a[i + j] = (x + y) % p;
    				a[i + j + d] = (x - y + p) % p;
    				a[i + j] = a[i + j] * inv % p;
    				a[i + j + d] = a[i + j + d] * inv % p;
    			}
    		}
    	}
    }
    
    int main()
    {
    	while (scanf("%d", &n) != EOF) {
    		for (int i = 0; i < n; i++) {
    			scanf("%d", &a[i]);
    		}
    		for (int i = 0; i < n; i++) {
    			scanf("%d", &b[i]);
    		}
    		fwt(a);
    		fwt(b);
    		for (int i = 0; i < n; i++) {
    			a[i] = (b[i] * qpow(a[i], p - 2)) % p;
    		}
    		ifwt(a);
    		for (int i = 0; i < n; i++) {
    			cout << a[i] << endl;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    P4717 【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT)

    一个将FWT的and,or,xor运算都包含的题目。挺好的,一下把板子搞全了

    #include 
    #define int long long
    using namespace std;
    const int mod =998244353;
    int n;
    const int N = 1<<17|1;
    int a[N];
    int b[N];
    int A[N],B[N];
    void in()
    {
        for(int i=0;i<n;i++)
            a[i]=A[i];
        for(int i=0;i<n;i++)
            b[i]=B[i];
    }
    void get()
    {
        for(int i=0;i<n;i++)
            a[i]*=b[i];
    }
    int fastpow(int n,int a)
    {
        int res=1;
        while(n)
        {
            if(n&1)
                res=res*a%mod;
            n>>=1;
            a=a*a%mod;
        }
        return res%mod;
    }
    void FWT_or(int *a,int op)
    {
    	for(int i=1;i<n;i<<=1)
        {
    		for(int p=i<<1,j=0;j<n;j+=p)
    		{
    			for(int k=0;k<i;k++)
    			{
    				(a[i+j+k]+=a[j+k]*op+mod)%=mod;
    			}
    		}
    	}
    }
    
    void FWT_and(int *a,int op)
    {
    	for(int i=1;i<n;i<<=1)
        {
    		for(int p=i<<1,j=0;j<n;j+=p)
    		{
    			for(int k=0;k<i;k++)
    			{
    				(a[j+k]+=a[i+j+k]*op+mod)%=mod;
    			}
    		}
    	}
    }
    
    void FWT_xor(int *a,int op)
    {
        int inv2=fastpow(mod-2,2);
    	for(int i=1;i<n;i<<=1)
        {
    		for(int p=i<<1,j=0;j<n;j+=p)
            {
    			for(int k=0;k<i;k++)
    			{
    				int X=a[j+k],Y=a[i+j+k];
    				a[j+k]=(X+Y)%mod;
    				a[i+j+k]=(X-Y+mod)%mod;
    				if(op==-1)
    				(a[j+k]*=inv2)%=mod,(a[i+j+k]*=inv2)%=mod;
    			}
    		}
    	}
    }
    void out()
    {
        for(int i=0;i<n;i++)
            cout<<(a[i]+mod)%mod<<" ";
        cout<<endl;
    }
    signed main()
    {
        cin>>n;
        int temp=1;
        temp<<=n;
        n=temp;
        for(int i=0;i<n;i++)
            cin>>A[i];
        for(int i=0;i<n;i++)
            cin>>B[i];
        in();FWT_or(a,1);FWT_or(b,1);get();FWT_or(a,-1);out();
        in();FWT_and(a,1);FWT_and(b,1);get();FWT_and(a,-1);out();
        in();FWT_xor(a,1);FWT_xor(b,1);get();FWT_xor(a,-1);out();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    P3809 【模板】后缀排序

    明天开刷后缀数组的题啦,今天吧LCP的性质/求法和SA的定义/求法看了看,写个模板题

    SA【i】:排名为i的后缀的第一个字符在原串中的下标
    rk[i]:起始字母下表在原串中为i的后缀的排名。
    suff(j):以原串第j个字母为首字母的后缀
    LCP(i,j):suff(sa[i])与suff(sa[j])的最长公共前缀(自己绕一下)

    LCP的四个性质
    1.LCP(i,j)=LCP(j,i);
    2.LCP(i,i)=len(sa[i])=n-sa[i]+1;
    以上两条十分自然,第一条是显然的,第二条就是求了个排名为i的后缀长度
    **
    3.LCP(i,k)=min(LCP(i,j),LCP(j,k)) 对于任意1<=i<=j<=k<=n
    4.LCP(i,k)=min(LCP(j,j-1)) 对于任意1 **
    复杂度分析:如果使用快排,复杂度是n(logn)2,这里使用基数排序,复杂度为nlogn

    #include 
    #define ini inline int
    #define maxn 1000050
    using namespace std;
    char s[maxn];
    int y[maxn],x[maxn],c[maxn],sa[maxn],rk[maxn],height[maxn],wt[30];
    int n,m;
    void get_SA()
    {
    	for (int i=1;i<=n;++i)
        {
            x[i]=s[i];
            ++c[x[i]];
    	//c数组是桶
    	//x[i]是第i个元素的第一关键字
        }
        for (int i=2;i<=m;++i)
            c[i]+=c[i-1];
        //做c的前缀和,我们就可以得出每个关键字最多是在第几名
        for(int i=n;i>=1;--i)
            sa[c[x[i]]--]=i;
        for (int k=1;k<=n;k<<=1)
        {
            int num=0;
            for(int i=n-k+1;i<=n;++i)
            y[++num]=i;
            //y[i]表示第二关键字排名为i的数,第一关键字的位置
    		//第n-k+1到第n位是没有第二关键字的 所以排名在最前面
            for (int i=1;i<=n;++i)
            if (sa[i]>k)
            y[++num]=sa[i]-k;
            //排名为i的数 在数组中是否在第k位以后
    		//如果满足(sa[i]>k) 那么它可以作为别人的第二关键字,就把它的第一关键字的位置添加进y就行了
    		//所以i枚举的是第二关键字的排名,第二关键字靠前的先入队
            for (int i=1;i<=m;++i)
             c[i]=0;
            //初始化c桶
            for (int i=1;i<=n;++i)
            ++c[x[i]];
            //因为上一次循环已经算出了这次的第一关键字 所以直接加就行了
            for (int i=2;i<=m;++i)
            c[i]+=c[i-1];//第一关键字排名为1~i的数有多少个
            for (int i=n;i>=1;--i)
            sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);
            x[sa[1]]=1;num=1;
            for (int i=2;i<=n;++i)
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num;
            //因为sa[i]已经排好序了,所以可以按排名枚举,生成下一次的第一关键字
            if (num==n) break;
            m=num;
            //这里就不用那个122了,因为都有新的编号了
        }
        for(int i=1;i<=n;++i)
        {
            cout<<sa[i]<<" ";
        }
    }
    void get_height()
    {
        int k=0;
        for (int i=1;i<=n;++i)
            rk[sa[i]]=i;
        for (int i=1;i<=n;++i)
        {
            if (rk[i]==1) continue;//第一名height为0
            if (k) --k;//h[i]>=h[i-1]+1;
            int j=sa[rk[i]-1];
            while (j+k<=n && i+k<=n && s[i+k]==s[j+k]) ++k;
            height[rk[i]]=k;//h[i]=height[rk[i]];
        }
    }
    int main()
    {
        gets(s+1);
        n=strlen(s+1);m=122;
        get_SA();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    CF1659C
    题意:给出一个n*m大小的1,-1矩阵,从左上角走到右下角,能否有种方法让所有步踩到的地砖上数字的和为0

    想法和做法都很常规,首先最大值小于0和最小值大于0肯定pass掉,其次更换路线最少会让原来的总和变化:0,2,-2三种情况。所以在最大值大于0并且最小值小于0的情况下,如果最大值不能被2整除还是不能够打成条件。

    最大值和最小值一眼dp维护没什么好说的。

    #include 
    
    using namespace std;
    const int N = 1e3+100;
    int grid[N][N],mn[N][N],mx[N][N];
    int main()
    {
        int t,n,m;
        for(cin>>t;t;t--)
        {
            cin>>n>>m;
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=m;j++)
                {
                    cin>>grid[i][j];
                }
            }
            mn[1][1]=mx[1][1]=grid[1][1];
            for(int i=2;i<=n;i++)
                mx[i][1]=mn[i][1]=mx[i-1][1]+grid[i][1];
            for(int j=2;j<=m;j++)
                mx[1][j]=mn[1][j]=mx[1][j-1]+grid[1][j];
            for(int i=2;i<=n;i++)
            {
                for(int j=2;j<=m;j++)
                {
                    mx[i][j]=max(mx[i-1][j],mx[i][j-1])+grid[i][j];
                    mn[i][j]=min(mn[i-1][j],mn[i][j-1])+grid[i][j];
                }
            }
            if(mx[n][m]<0||mn[n][m]>0||mx[n][m]%2)
            {
                cout<<"NO"<<endl;
            }
            else
            {
                cout<<"YES"<<endl;
            }
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
  • 相关阅读:
    SpringCloud中服务间通信方式以及Ribbon、Openfeign组件的使用
    ImportError: /lib64/libstdc++.so.6: version `CXXABI_1.3.9‘ not found的解决方法
    SPA项目开发之登录注册
    【刷题笔记10.6】LeetCode:翻转二叉树
    【Android安全】Binder解析
    【开发】安防监控/视频存储/视频汇聚平台EasyCVR优化播放体验的小tips
    Shell 文本三剑客 awk
    阿里云杨皓然:Serverless或将引领云的下一个时代
    剖析一下"抢茅台"脚本底层逻辑
    分布式事务详解
  • 原文地址:https://blog.csdn.net/qq_35866893/article/details/126065133