2021.11.11 模拟赛总结

Ⅰ. 串串串

问题描述:

你有两个长度分别为 n,m 的 01 串 S,T 。

有 Q 次询问,每次询问给出 l1,r1,l2,r2 ,满足 r1−l1+1=r2−l2+1 。令 a=S[l1…r1] , b=T[l2…r2] ,你需要求出 ai≠bi 的位置个数对 2 取模的结果。

输入格式:

第一行两个正整数 n,m ,分别表示 S,T 的长度。

接下来两行输入两个 01 串表示 S 和 T 。

接下来一行一个整数 Q ,表示询问的个数。

接下来 Q 行,每行四个整数 l1,r1,l2,r2, 表示一组询问。

输出格式:

对于每组询问,输出一个数 01 表示答案。

数据范围:

对于 30% 的数据: n,m,q≤5000 ;
对于 70% 的数据: n,m,q≤5×104 ;
对于 80% 的数据: n,m,q≤105 ;
对于 100% 的数据: 1≤n,m,q≤2×1051≤l1≤r1≤n , 1≤l2≤r2≤m 。

 

 

  注意到这道题的答案对 2 取模,即,只需回答奇偶性。

 

  字符串 a 、b 存在以下三种情况:

    1. '0' 和 '1' :贡献为 1 ;

    2. '0' 和 '0' :贡献为 0;

    3. '1' 和 '1' :贡献为 0,但与 2 奇偶性相同。

 

  所以:我们只需要统计区间中 1 的个数的奇偶性就可以。

 

Code:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
int n,m,q,A[200005],B[200005];
#define gc (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1,*p2;
inline int read()
{
    char ch;int x(0);while((ch=gc)<48);
    do x=x*10+ch-48;while((ch=gc)>=48);
    return x;
}
inline int Read()
{
    char ch;int x(0);while((ch=gc)<48);
    x=ch-48;return x;
}
int main()
{
    n=read(),m=read();
    for(register int i=1;i<=n;++i) A[i]=A[i-1]+Read();
    for(register int i=1;i<=m;++i) B[i]=B[i-1]+Read();
    q=read();
    for(register int i=1,l,r,ll,rr;i<=q;++i)
    l=read(),r=read(),ll=read(),rr=read(),printf("%d\n",(A[r]-A[l-1]+B[rr]-B[ll-1])&1);
    return 0;
}
串串串

 


 

Ⅱ. 方格计数

问题描述:

在左下角是 (0,0) ,右上角是 (W,H) 的网格上,有 (W+1)×(H+1) 个格点。

现在要在格点上找 N 个不同的点,使得这些点在一条直线上。并且在这条直线上,相邻点之间的距离不小于 D 。

求方案数模 109+7

输入格式:

第一行一个整数 T ,表示数据组数。

接下来 T 行,每行四个整数 N,W,H,D ,意义如题目描述。

输出格式:

T 行, 每行一个整数表示答案。

数据范围:

对于 20% 的数据: N,W,H,D≤10 。
对于 50% 的数据: W,H,D≤100 。
对于另 20% 的数据: N≤5 。
对于 100% 的数据: 1≤N≤501≤W,H,D≤5001≤T≤20

 

 

前置:

  1. 在一个以 (0,0) 开始的二维网格中,一条线段 (0,0)−(x,y) 含有整数点的个数为 gcd(x,y)−1(不包含线段的两个整数端点)

  2. 有 n个盒子,从中选 m 个,且相邻两个盒子之间至少有 k 个盒子的组合方案为 :

 

 

  首先,暴力很好想,枚举两个端点,横坐标之差的绝对值为 x ,纵坐标之差的绝对值为 y ,那么这里面包含的整数点个数为 gcd( x , y ) - 1 ,强制两个端点必须选,那么剩下还要选 n−2 个,

  求出相邻两个盒子之间编号差至少为 k,即两个盒子之间的盒子个数至少为k−1,两个端点选了会导致两个 k−1 的长度区间盒子不能选 ,剩下的盒子数只有 g−1−2(k−1)

  套用上面的组合数公式。不难发现,最后只与横纵坐标差有关,所以直接枚举横纵坐标差,乘以情况数 (w−x+1)(h−y+1) 。

  但是这个差是绝对值差,所以如果不是水平或竖直线,就有两种情况。

Code:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
int _,n,w,h,d,C[505][505],Ans;
#define gc (p1==p2&&(p2=(p1=buf)+fread(buf,1,65536,stdin),p1==p2)?EOF:*p1++)
char buf[65536],*p1,*p2;
inline int read()
{
    char ch;int x(0);while((ch=gc)<48);
    do x=x*10+ch-48;while((ch=gc)>=48);
    return x;
}
inline void Pre()
{
    for(register int i=0;i<=500;++i)
        {
            C[i][0]=C[i][i]=1;
            for(register int j=1;j<i;++j) C[i][j]=(1LL*C[i-1][j]+C[i-1][j-1])%mod;
        }
}
inline int Gcd(int x,int y) {return y?Gcd(y,x%y):x;}
inline double Work(int x,int y) {return sqrt(x*x*1.0+y*y);}
inline int Get(int x,int y)
{
    if(!x&&!y) return 0;int g=Gcd(x,y);
    int k=(int)ceil(d/Work(x/g,y/g));if(k*(n-1)>g) return 0;
    int RET=C[g-1-2*(k-1)-(k-1)*(n-3)][n-2];
    if(x&&y) RET=((1LL*RET)<<1)%mod;
    return 1LL*RET*(w-x+1)%mod*(h-y+1)%mod;
}
signed main()
{
    _=read(),Pre();
    for(register int __=1;__<=_;++__)
    {
        n=read(),w=read(),h=read(),d=read(),Ans=0;
        if(n==1) {printf("%lld\n",(w+1)*(h+1));continue;}
        for(register int i=0;i<=w;++i)
            for(register int j=0;j<=h;++j) Ans=(1LL*Ans+Get(i,j))%mod;
        printf("%lld\n",Ans);
    }
    return 0;
}
方格计数

 


 

Ⅲ. 树数树

问题描述:

 输入格式:

第一行一个正整数 T ,表示数据组数。

对于每组数据第一行一个正整数 n 。

接下来 n−1 行, 每行两个正整数 u,v ,表示树上的一条边。

输出格式:

T 行, 每行一个整数表示每组数据的答案。

数据范围:

对于 100% 的数据: 1≤T≤52≤n≤10e5 , 1≤u,v≤n , u≠v ,输入保证是一棵树。

 

  首先,不难发现:对于一条链或一颗二叉树,所有节点是可以选完的;而菊花图最多只能选出 3 个点;

  不难推出:x 的最大贡献为其子树中最大两个贡献之和加 1 ;

  

  考虑怎么维护?

   很容易想道优先队列(堆)(当然,线段树也可),对每个点进行维护,注意要用 “ 启发式合并 ” 。

 

Code:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
int _,n,w,h,d,C[505][505],Ans;
#define gc (p1==p2&&(p2=(p1=buf)+fread(buf,1,65536,stdin),p1==p2)?EOF:*p1++)
char buf[65536],*p1,*p2;
inline int read()
{
    char ch;int x(0);while((ch=gc)<48);
    do x=x*10+ch-48;while((ch=gc)>=48);
    return x;
}
inline void Pre()
{
    for(register int i=0;i<=500;++i)
        {
            C[i][0]=C[i][i]=1;
            for(register int j=1;j<i;++j) C[i][j]=(1LL*C[i-1][j]+C[i-1][j-1])%mod;
        }
}
inline int Gcd(int x,int y) {return y?Gcd(y,x%y):x;}
inline double Work(int x,int y) {return sqrt(x*x*1.0+y*y);}
inline int Get(int x,int y)
{
    if(!x&&!y) return 0;int g=Gcd(x,y);
    int k=(int)ceil(d/Work(x/g,y/g));if(k*(n-1)>g) return 0;
    int RET=C[g-1-2*(k-1)-(k-1)*(n-3)][n-2];
    if(x&&y) RET=((1LL*RET)<<1)%mod;
    return 1LL*RET*(w-x+1)%mod*(h-y+1)%mod;
}
signed main()
{
    _=read(),Pre();
    for(register int __=1;__<=_;++__)
    {
        n=read(),w=read(),h=read(),d=read(),Ans=0;
        if(n==1) {printf("%lld\n",(w+1)*(h+1));continue;}
        for(register int i=0;i<=w;++i)
            for(register int j=0;j<=h;++j) Ans=(1LL*Ans+Get(i,j))%mod;
        printf("%lld\n",Ans);
    }
    return 0;
}
树数树

 

posted @ 2021-11-11 21:04  qfxl  阅读(208)  评论(0)    收藏  举报