1. 程式人生 > 其它 >Noip模擬74 2021.10.12

Noip模擬74 2021.10.12

T1 自然數 T2 錢倉 T3 遊戲 T4 Sanrd

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;
 7
++r; ans+=pos; 8 } 9 while(top) vis[stk[top--]]=0; 10 } 11 write(ans);
TLE 50

那麼考慮優化右端點移動的過程

先把$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

咕咕咕