NOIP模擬12
阿新 • • 發佈:2021-07-12
T1:簡單的區間
Description
基本思路:
區間問題,我最不擅長了
考慮分治:
我們首先可以\(O(n)\)用單調棧搞出沒一個值作為最大值的區間的左右端點,並且可以處理出第i位字首和%k的餘數並記錄,用一個vector,空間複雜度為\(O(n)\),可以接受,由於我們是順序遍歷,所以在每一個vector中下標是升序的。
然後我們遍歷\(1~n\),處理包含\(a[i]\)在內的區間
我們不必左右兩個區間都列舉。只需列舉較短的區間,然後在vector中利用lower_bound與upper_bound\(O(logn)\)查詢即可;
上程式碼:
#include<bits/stdc++.h> using namespace std; namespace STD { #define ll long long #define rr register #define inf INT_MAX #define max(x,y) (x>y?x:y) const int SIZE=3e5+4; const int SIZEK=1e6+4; int n,k; ll ans; ll a[SIZE]; int f[SIZE]; int l[SIZE],r[SIZE];//l<r vector<int> b[SIZEK]; int read() { rr int x_read=0,y_read=1; rr char c_read=getchar(); while(c_read<'0'||c_read>'9') { if(c_read=='-') y_read=-1; c_read=getchar(); } while(c_read<='9'&&c_read>='0') { x_read=x_read*10+(c_read^48); c_read=getchar(); } return x_read*y_read; } struct pack { int fi,se; pack(){} pack(int fi_,int se_){this->fi=fi_;this->se=se_;} }; class Stack { private: pack *Top; pack s[SIZE]; public: Stack(){Top=s;} void push(pack x){*(++Top)=x;} void pop(){Top--;} bool empty(){return Top==s;} pack top(){return *Top;} }s; }; using namespace STD; int main() { n=read(),k=read(); b[0].push_back(0); //一定要提前把0壓入原因見下 for(rr int i=1;i<=n;i++) { a[i]=read(); f[i]=(f[i-1]+a[i]%k)%k; b[f[i]].push_back(i); } //單調棧 for(rr int i=1;i<=n;i++) { if(s.empty()){s.push(pack(a[i],i));continue;} if(s.top().fi>=a[i]){s.push(pack(a[i],i));continue;} while(!s.empty()&&s.top().fi<a[i]) { r[s.top().se]=i-1; s.pop(); } s.push(pack(a[i],i)); } while(!s.empty()){r[s.top().se]=n;s.pop();} for(rr int i=n;i;i--) { if(s.empty()){s.push(pack(a[i],i));continue;} if(s.top().fi>a[i]){s.push(pack(a[i],i));continue;} while(!s.empty()&&s.top().fi<=a[i]) { l[s.top().se]=i+1; s.pop(); } s.push(pack(a[i],i)); } while(!s.empty()){l[s.top().se]=1;s.pop();} // //這個統計ans的式子是依據%k相同的數相減可以被k整除 //就有j[r]-j[l-1]-a[i]==0 //下面num的計算是依據這個式子移項來的 //注意觀察與思考lower_bound第3個引數 for(rr int i=1;i<=n;i++) { if(l[i]==r[i]) continue;//l<r //以i作為某一端點 int num,st,en; if(i-l[i]) { num=((f[i]-a[i]%k)+k)%k; st=lower_bound(b[num].begin(),b[num].end(),l[i]-1)-b[num].begin(); en=upper_bound(b[num].begin(),b[num].end(),i-2)-b[num].begin(); //一定要注意b[num]可能為空,如果不判空就呼叫會RE //為因為這個沒看出來調了一下午,這下要長記性了QAQ。 if(b[num].size()&&b[num][st]<=i-2) ans+=(en-st); } if(r[i]-i) { num=(f[i-1]+a[i]%k)%k; st=lower_bound(b[num].begin(),b[num].end(),i+1)-b[num].begin(); en=upper_bound(b[num].begin(),b[num].end(),r[i])-b[num].begin(); if(b[num].size()&&b[num][st]<=r[i]) ans+=(en-st); } if(!(i-l[i])||!(r[i]-i)) continue; if(i-l[i]<r[i]-i) { for(rr int j=l[i]-1;j<i-1;j++) { num=(f[j]+a[i]%k)%k; st=lower_bound(b[num].begin(),b[num].end(),i+1)-b[num].begin(); en=upper_bound(b[num].begin(),b[num].end(),r[i])-b[num].begin(); if(b[num].size()&&b[num][st]<=r[i]) ans+=(en-st); } } else { for(rr int j=i+1;j<=r[i];j++) { num=((f[j]-a[i]%k)+k)%k; st=lower_bound(b[num].begin(),b[num].end(),l[i]-1)-b[num].begin(); en=upper_bound(b[num].begin(),b[num].end(),i-2)-b[num].begin(); if(b[num].size()&&b[num][st]<=i-2) ans+=(en-st); } } } printf("%lld",ans); }
T2:簡單的玄學
Description:
很明顯,直接求是不可能的,所以我們要求\(1-P(一個相等的也沒有)\)。
式子就是:
其實式子很好推,但是難計算,考場上就是因為不會高效計算WA了。
其實可以觀察,當m或n比較大時,分子是有可能變成1e6+3的倍數的,所以我們當乘到是模數的倍數時,就跳出,因為還有個“1-”呢,總不可能輸出一吧;
2的冪次直接快速冪即可。
至於說約分。
我們可以發現,分母的因子全是2.
因此統計分子有多少個2因子即可
有一個性質:
\(2^n-x\)的2因子個數等於\(x\)中的個數,證明:
\[2^{n}-x= \]\[2^{m}*(2^{n-m}-j) \]
假設\(x=2^{m}*j\),\(j\)可以是分數顯然\(j\)沒有2因子,因而\(2^{n-m}-j\)沒有2因子,因而該結論正確
又由於連乘因子無損失,因此我們直接求\(!(m-1)\)的2因子個數即可
這有一個\(O(logn)\)求法,詳見程式碼;
分子約分後值求解直接暴力計算即可。
上程式碼:
#include<bits/stdc++.h> using namespace std; namespace STD { #define ll long long #define rr register #define inf INT_MAX const ll mod=1e6+3; ll n,m; ll read() { rr ll x_read=0,y_read=1; rr char c_read=getchar(); while(c_read<'0'||c_read>'9') { if(c_read=='-') y_read=-1; c_read=getchar(); } while(c_read<='9'&&c_read>='0') { x_read=x_read*10+(c_read^48); c_read=getchar(); } return x_read*y_read; } ll qpow(ll base,ll exp) { ll ret=1; while(exp) { if(exp&1) ret=ret*base%mod; base=base*base%mod; exp>>=1; } return ret; } }; using namespace STD; int main() { n=read(),m=read(); ll cnt=0; for(ll i=1;(1ll<<i)<=(m-1);i++) cnt+=((m-1)/(1ll<<i)); ll b=qpow(2,n); ll n2=b; ll n3; b=qpow(b,m-1); n3=b; ll x=qpow(2,cnt); x=qpow(x,mod-2); b=b*x%mod; ll a=1ll; for(rr ll i=1;i<=m-1;i++) { a=a*((n2-i%mod)%mod)%mod; if(!a) break; } a=a*x%mod; a=(b-a+mod)%mod;//這裡一定要注意用b-a再取模,因為b才是約分後的分母 printf("%lld %lld",a,b); }
T3:簡單的填數
沒調出來呢,先鴿掉。