Noip模擬74 2021.10.12
T1 自然數
考場上當我發現我的做法可能要打線段樹的時候,以為自己百分之百是考慮麻煩了
但還是打了,還過掉了所有的樣例,於是十分自信的就交了
正解還真是線段樹,真就第一題資料結構
但是包括自己造的小樣例還是大樣例我都少考慮了一個問題,就是元素可能相等
所以估分$100$實際只有$68$,以後造樣例的時候還是要根據題目搞一些極端的資料出來
先說暴力吧,考慮到$a_i$的值域是拿來嚇唬人的,因為$mex$不可能超過$2e5$
所以大於邊界的值直接跳過不考慮,這個條件比較關鍵
那麼我們使用經典指標
$pos$指向答案,初始指向$0$,列舉左端點掃右端點
那麼每次使用棧記錄遍歷到的$a_i$值,以便清空時更快
顯然只有當掃到的點的值把當前$pos$的位置佔了之後$pos$才會移動
所以維護指標的演算法就出來了,$O(n^2)$
1 for(int l=1;l<=n;l++){ 2 int pos=0,r=l; 3 while(vis[pos]) ++pos; 4 while(r<=n){ 5 if(a[r]<NN&&(!vis[a[r]])) vis[a[r]]=1,stk[++top]=a[r]; 6 while(vis[pos]) ++pos; 7TLE 50++r; ans+=pos; 8 } 9 while(top) vis[stk[top--]]=0; 10 } 11 write(ans);
那麼考慮優化右端點移動的過程
先把$1~[1,N]$的所有區間的貢獻預處理出來,用一個數組記錄,為$h[r]$
拿我自己造的優秀$hack$資料舉例
5 1 2 0 3 5 //其實還有 5 2 1 0 3 5 && 5 3 1 0 2 5
以上三個答案分別是$17,20,21$
以第二個來說的話,預處理出的陣列為$0,0,3,4,4$,
然後考慮每次刪除一個左端點,發現貢獻會依次變為
$0,2,2,2$、$1,1,1$、$0,0$、$0$,然後你可以再試一下有重複數字的陣列
發現$a[l]$(當前要刪的左端點值)會把第一個大於它的數($h$數組裡的)到下一個它出現($a$數組裡的)之間的所有數覆蓋成$a[l]$這個值
想一想的話也很好理解(基於你打了雙指標移動的暴力)
所以你記錄一個$nxt$陣列,線段樹上維護一個$sum,minn$就可以解決問題了,找位置用線段樹二分
因為我是沒發現有重複,所以直接更新到$n$他是對的,於是我直接在沒修改的$h$陣列上面二分找位置也是對的,
因為每次更新只會往小裡更新,而且更新之後陣列還是單調不降。
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 const int NN=400005; 15 int n,a[NN],ans,sum,h[NN],nxt[NN]; 16 int vis[NN],tag[NN]; 17 struct SNOWtree{ 18 #define lid (id<<1) 19 #define rid (id<<1|1) 20 int ll[NN<<2],rr[NN<<2]; 21 int sum[NN<<2],laz[NN<<2],mn[NN<<2]; 22 inline void pushup(int id){ 23 if(ll[id]==rr[id]) return; 24 sum[id]=sum[lid]+sum[rid]; 25 mn[id]=min(mn[lid],mn[rid]); 26 } 27 inline void pushdown(int id){ 28 if(ll[id]==rr[id]||laz[id]==-1) return; 29 laz[lid]=laz[rid]=laz[id]; 30 mn[rid]=mn[lid]=laz[id]; 31 sum[lid]=laz[id]*(rr[lid]-ll[lid]+1); 32 sum[rid]=laz[id]*(rr[rid]-ll[rid]+1); 33 laz[id]=-1; 34 } 35 inline void build(int id,int l,int r){ 36 ll[id]=l; rr[id]=r; laz[id]=-1; mn[id]=0x3fffffff; 37 if(l==r) return mn[id]=sum[id]=h[l],void(); 38 int mid=l+r>>1; 39 build(lid,l,mid); build(rid,mid+1,r); 40 pushup(id); 41 } 42 inline void change(int id,int pos,int v){ 43 if(ll[id]==rr[id]) return mn[id]=sum[id]=v,void(); 44 pushdown(id);int mid=ll[id]+rr[id]>>1; 45 if(pos<=mid) change(lid,pos,v); 46 else change(rid,pos,v); 47 pushup(id); 48 } 49 inline void update(int id,int l,int r,int v){ 50 if(l<=ll[id]&&rr[id]<=r){ 51 sum[id]=v*(rr[id]-ll[id]+1); 52 mn[id]=laz[id]=v; return; 53 }pushdown(id);int mid=ll[id]+rr[id]>>1; 54 if(l<=mid) update(lid,l,r,v); 55 if(r>mid) update(rid,l,r,v); 56 pushup(id); 57 } 58 inline int lower_bound_(int id,int pos){ 59 if(ll[id]==rr[id]){ 60 if(mn[id]>=pos) return ll[id]; 61 return n+1; 62 }pushdown(id); 63 if(mn[rid]<pos) return lower_bound_(rid,pos); 64 else return min(lower_bound_(lid,pos),ll[rid]); 65 return n+1; 66 } 67 }tr; 68 namespace WSN{ 69 inline short main(){ 70 // freopen("in.in","r",stdin); 71 // freopen("bao.out","w",stdout); 72 freopen("mex.in","r",stdin); 73 freopen("mex.out","w",stdout); 74 n=read(); 75 for(int i=1;i<=n;i++) a[i]=read(); 76 int pos=0; 77 for(int r=1;r<=n;r++){ 78 if(a[r]<NN&&(!vis[a[r]])) vis[a[r]]=1; 79 while(vis[pos]) ++pos; 80 sum+=pos; h[r]=pos; nxt[r]=n+1; 81 } 82 memset(vis,0,sizeof(vis)); 83 for(int i=1;i<=n;i++) if(a[i]<NN) nxt[vis[a[i]]]=i,vis[a[i]]=i; 84 // for(int i=1;i<=n;i++) cout<<nxt[i]<<" ";cout<<endl; 85 // cout<<sum<<endl; 86 ans=sum; tr.build(1,1,n); 87 for(int l=1;l<n;l++){ 88 tr.change(1,l,0); 89 pos=tr.lower_bound_(1,a[l]+1); 90 // cout<<pos<<" "<<nxt[pos]<<endl; 91 if(pos<nxt[l]) tr.update(1,pos,nxt[l]-1,a[l]); 92 ans+=tr.sum[1]; 93 } 94 write(ans); 95 return 0; 96 } 97 } 98 signed main(){return WSN::main();} 99 // cout<<a[l-1]<<" "<<pos<<" "<<tr.query(1,l,pos-1)<<" "<<(n-pos+1)*a[l-1]<<endl;View Code
T2 錢倉
按照$c_i-1$的字首和找到最長的$\geq 0$的一段,記一個起點$S$然後倒著貪心把有東西的點上的東西能向後移動就向後移動
維護一個佇列就行
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 const int NN=2e5+5; 15 int n,c[NN],S,sum; 16 int q[NN],l,r; 17 namespace WSN{ 18 inline short main(){ 19 // freopen("in.in","r",stdin); 20 // freopen("bao.out","w",stdout); 21 freopen("barn.in","r",stdin); 22 freopen("barn.out","w",stdout); 23 n=read(); 24 for(int i=1;i<=n;i++) c[n+i]=c[i]=read(); 25 for(int i=1;i<=n;i++){ 26 sum+=c[i]-1; 27 if(sum<0) S=i+1,sum=0; 28 } 29 l=1,r=sum=0; 30 for(int i=S+n-1;i>=S;i--){ 31 if(!c[i]) q[++r]=i; 32 else{ 33 while(l<=r && c[i]){ 34 int pos=q[l++]; --c[i]; 35 sum+=(pos-i)*(pos-i); 36 } 37 if(!c[i]) q[++r]=i; 38 } 39 } write(sum); 40 return 0; 41 } 42 } 43 signed main(){return WSN::main();}View Code
T3 遊戲
設$a_n,b_n$分別為剩下$n$個石子時$A$先手獲勝的概率和$B$先手獲勝的概率
發現$n$為奇數的時候兩人都想拿,偶數時都不想
那麼有兩個遞推方程
$a_{n+1}=p(1-q)a_n+(1-p)b_n+(1-p)(1-q)a_{n+1}$
$b_{n+1}=q(1-p)b_n+(1-q)a_n+(1-p)(1-q)b_{n+1}$
然後化簡按照奇數偶數轉移就有$76$分
然後把轉移係數構建矩陣就可以了
具體就是把奇數矩陣先和偶數矩陣乘在一起,然後做$\frac{n}{2}$次轉移
如果$n$是奇數再來一次奇數矩陣,
最後直接輸出第二行第一列的值即可,因為$a_0=0,b_0=1$,轉移只有那一個有貢獻
複雜度$O(8log_nT)$
1 #include<stdio.h> 2 #include<algorithm> 3 #include<cstring> 4 #define int long long 5 using namespace std; 6 const int mod=1e9+7,inv=571428574; 7 int T,n,p,q; 8 inline int qmo(int a,int b,int ans=1){ 9 int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c; 10 return ans; 11 } 12 namespace Matrix{ 13 struct Ma{ 14 int m[3][3]; 15 Ma(){memset(m,0,sizeof(m));} 16 inline void pre(){m[1][1]=m[2][2]=1;} 17 inline void print(){printf("%lld %lld\n%lld %lld\n",m[1][1],m[1][2],m[2][1],m[2][2]);} 18 Ma operator*(const Ma&a)const{ 19 Ma res; 20 for(int i=1;i<=2;i++) 21 for(int j=1;j<=2;j++) 22 for(int k=1;k<=2;k++) 23 (res.m[i][j]+=m[i][k]*a.m[k][j]%mod)%=mod; 24 return res; 25 } 26 }; 27 inline Ma ksm(Ma a,int b){ 28 Ma ans;ans.pre(); 29 for(;b;b>>=1,a=a*a)if(b&1)ans=ans*a; 30 return ans; 31 } 32 int c[3]; 33 inline void mul(int a[3],Ma b){ 34 memset(c,0,sizeof(c)); 35 for(int i=1;i<=2;i++) 36 for(int j=1;j<=2;j++) 37 (c[i]+=a[j]*b.m[j][i]%mod)%=mod; 38 memcpy(a,c,sizeof(c)); 39 } 40 }using namespace Matrix; 41 namespace TASK{ 42 inline void task1(){ 43 int P=p*inv%mod,Q=q*inv%mod; 44 printf("%lld\n",P*qmo((1-((1-P+mod)%mod)*((1-Q+mod)%mod)%mod+mod)%mod,mod-2)%mod); 45 } 46 inline void task2(){ 47 int P=p*inv%mod,Q=q*inv%mod,v3=qmo(3,mod-2);Ma g; 48 g.m[2][2]=g.m[1][1]=v3;g.m[2][1]=g.m[1][2]=2*v3%mod; 49 g=ksm(g,n);printf("%lld\n",g.m[2][1]); 50 } 51 int a[1005],b[1005]; 52 inline void task3(){ 53 memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); 54 a[0]=0; b[0]=1; 55 int P=p*inv%mod,Q=q*inv%mod; 56 for(int i=1;i<=n;i++){ 57 if((i&1)==0){ 58 int tmp=qmo((1-P*Q%mod+mod)%mod,mod-2); 59 a[i]=P*((1-Q+mod)%mod)%mod*tmp%mod*a[i-1]%mod+(1-P+mod)%mod*tmp%mod*b[i-1]%mod; 60 b[i]=(1-Q+mod)%mod*tmp%mod*a[i-1]%mod+Q*((1-P+mod)%mod)%mod*tmp%mod*b[i-1]%mod; 61 }else{ 62 int tmp=qmo((1-((1-P+mod)%mod)*((1-Q+mod)%mod)%mod+mod)%mod,mod-2); 63 a[i]=(1-P+mod)%mod*Q%mod*tmp%mod*a[i-1]%mod+P*tmp%mod*b[i-1]%mod; 64 b[i]=Q*tmp%mod*a[i-1]%mod+(1-Q+mod)%mod*P%mod*tmp%mod*b[i-1]%mod; 65 } 66 } 67 printf("%lld\n",a[n]%mod); 68 } 69 }using namespace TASK; 70 int dp[3]; 71 namespace WSN{ 72 inline short main(){ 73 freopen("game.in","r",stdin); 74 freopen("game.out","w",stdout); 75 scanf("%lld",&T); 76 while(T--){ 77 scanf("%lld%lld%lld",&n,&p,&q); 78 Ma f,g; 79 int P=p*inv%mod,Q=q*inv%mod; 80 int tmp=qmo((1-P*Q%mod+mod)%mod,mod-2); 81 f.m[1][1]=P*((1-Q+mod)%mod)%mod*tmp%mod; 82 f.m[1][2]=(1-Q+mod)%mod*tmp%mod; 83 f.m[2][1]=(1-P+mod)%mod*tmp%mod; 84 f.m[2][2]=Q*((1-P+mod)%mod)%mod*tmp%mod; 85 tmp=qmo((1-((1-P+mod)%mod)*((1-Q+mod)%mod)%mod+mod)%mod,mod-2); 86 g.m[1][1]=(1-P+mod)%mod*Q%mod*tmp%mod; 87 g.m[1][2]=Q*tmp%mod; 88 g.m[2][1]=P*tmp%mod; 89 g.m[2][2]=(1-Q+mod)%mod*P%mod*tmp%mod; 90 Ma ret=g*f; int b=n/2;dp[1]=0;dp[2]=1; 91 while(b){ 92 if(b&1) mul(dp,ret); 93 b>>=1; ret=ret*ret; 94 } 95 if(n&1) mul(dp,g); 96 printf("%lld\n",dp[1]%mod); 97 } 98 return 0; 99 } 100 } 101 signed main(){return WSN::main();}View Code
T4 Sanrd
咕咕咕