1. 程式人生 > 其它 >2021.11.11 模擬賽總結

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;
}
樹數樹