Loading

Noip模拟74 2021.10.11

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

咕咕咕

posted @ 2021-10-12 20:32  雪域亡魂  阅读(102)  评论(0编辑  收藏  举报