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, 表示一組詢問。
輸出格式:
對於每組詢問,輸出一個數 0 或 1 表示答案。
資料範圍:
對於 30% 的資料: n,m,q≤5000 ; 對於 70% 的資料: n,m,q≤5×104 ; 對於 80% 的資料: n,m,q≤105 ; 對於 100% 的資料: 1≤n,m,q≤2×105 , 1≤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≤50 , 1≤W,H,D≤500 , 1≤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≤5 , 2≤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; }樹數樹