[考试反思]0423省选模拟78:打卡
考了四个半小时,签了四个半小时的到。
吸取昨天的教训,打卡打卡,把签到分拿全然后就能混个名次啥的。
(其实$T2$还有$9$分的大力分类讨论是懒得写了,码量大分数少写它干啥
考场上尝试想$T2$的正解来着,结果事实证明它又是个结论题。。。全场最高分$16$就没啥好说的
$T1$拿完暴力就跑路,$T3$乖乖签到然后没再看一眼,总是选错题这可咋整。。
T1:Arithmetic
大意:对于任意一个数列,你可以花费$1$代价添加一个元素,$0$代价进行排序。设你把一个数列$A$经过操作变成以$d$为等差数列的代价为$f(A)$
多次询问求$\sum\limits_{i=l}^{r} \sum\limits_{j=l}^{r} f(s[l...r])$。特别的,如果怎么操作都不可能成为等差数列,$f(A)=0$。$n \le 3 \times 10^5,1 \le d,s_i \le 10^7$
首先比较明显的是,如果一个子区间有相同元素则不可能是等差数列。
其次,如果一个子区间内$\forall i,j$满足$A_i \equiv A_j \mod d$则合法否则也构造不出等差数列。
如果能构造出来,那么一个等差数列的代价就是$\frac{max-min}{d} -(r-l)$。
因为这样一个等差数列一共有$\frac{max-min}{d} +1$项。你已有的有$r-l+1$项。所以就是这个东西。
于是我们考虑用线段树维护答案。枚举右端点,线段树下标表示左端点,存储这样一个区间的代价。
离线询问后,发现需要做的就是查询历史版本和。
同时维护两个单调栈以更新区间最值。右端点每次右移的时候所有区间代价$-1$
然后发现如果以当前位置为右端点,某个左端点(及编号更小的左端点)非法了,就需要把前缀的代价都置为$0$
预处理对于每个左端点,第一个非法的右端点即可。
所以弄一个线段树,支持区间加,前缀清空,查询版本和。
假设我们在$t_i$时刻进行了一次区间加$h_i$,如果在查询的时候时间为$T$那么我们要查询的值就是
$\sum (T-t_i)h_i = T\sum h_i - \sum t_ih_i$
分别维护两节就行了。然后清空的话暴力清就可以(记录上一次清空的右端点,这次从$R+1$开始,同时所有操作的左端点都要与$R+1$取$max$)
每个点都只会被清一次。所以总复杂度还是$O(nlogn)$
有点卡常,没有懒标记的时候不要$down$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 1333333 4 #define ll long long 5 struct qs{int l,r,o;friend bool operator<(qs a,qs b){return a.r<b.r;}}Q[S]; 6 int RM[S],w[S],n,d,q,R[S],lst[10000001],mx[S],mn[S],mxt,mnt,mxp[S],mnp[S],R0,rm[S],T; 7 ll ans[S],B[S],A[S],lzA[S],lzB[S]; 8 #define lc p<<1 9 #define rc lc|1 10 #define md (L+R>>1) 11 void down(int p,int L,int R){ 12 if(!lzA[p]&&!lzB[p])return; 13 A[rc]+=lzA[p]*(R-md);A[lc]+=lzA[p]*(md-L+1); 14 B[rc]+=lzB[p]*(R-md);B[lc]+=lzB[p]*(md-L+1); 15 lzA[lc]+=lzA[p];lzB[lc]+=lzB[p];lzA[rc]+=lzA[p];lzB[rc]+=lzB[p]; 16 lzA[p]=lzB[p]=0; 17 } 18 void add(int l,int r,int d,int p=1,int L=1,int R=n){ 19 if(l<=L&&R<=r){A[p]+=d*(R-L+1ll);B[p]+=T*(R-L+1ll)*d;lzA[p]+=d;lzB[p]+=1ll*T*d;return;} 20 down(p,L,R); 21 if(l<=md)add(l,r,d,lc,L,md); if(r>md)add(l,r,d,rc,md+1,R); 22 A[p]=A[lc]+A[rc]; B[p]=B[lc]+B[rc]; 23 } 24 ll ask(int l,int r,int p=1,int L=1,int R=n){ 25 if(l<=L&&R<=r)return A[p]*T-B[p]; 26 down(p,L,R); 27 return (l<=md?ask(l,r,lc,L,md):0)+(r>md?ask(l,r,rc,md+1,R):0); 28 } 29 void reset(int l,int r,int p=1,int L=1,int R=n){ 30 if(L==R){B[p]-=A[p]*T;A[p]=0;return;} 31 down(p,L,R); 32 if(l<=md)reset(l,r,lc,L,md); if(r>md)reset(l,r,rc,md+1,R); 33 A[p]=A[lc]+A[rc]; B[p]=B[lc]+B[rc]; 34 } 35 void Add(int l,int r,int d){l=max(l,R0+1);if(l<=r)add(l,r,d);} 36 int main(){ 37 //freopen("arithmetic3.in","r",stdin);freopen("0.out","w",stdout); 38 cin>>n>>d>>q; R[0]=-1; mx[0]=10000001; 39 for(int i=1;i<=n;++i)scanf("%d",&w[i]),RM[lst[w[i]]]=i,lst[w[i]]=i,R[i]=w[i]%d,w[i]/=d,RM[i]=n+1; 40 for(int i=2;i<=n;++i)if(R[i]!=R[i-1])for(int j=i-1;R[j]==R[i-1];--j)RM[j]=min(RM[j],i); 41 for(int i=1;i<=n;++i)rm[RM[i]]=i; 42 for(int i=1,l,r;i<=q;++i)scanf("%d%d",&l,&r),Q[i]=(qs){l,r,i}; 43 sort(Q+1,Q+1+q); int _=1; 44 for(T=1;T<=n;++T){ 45 if(rm[T]>R0)reset(R0+1,rm[T]),R0=rm[T]; 46 Add(1,T-1,-1); 47 while(mx[mxt]<w[T])Add(mxp[mxt-1]+1,mxp[mxt],-mx[mxt]),mxt--; mx[++mxt]=w[T]; mxp[mxt]=T; Add(mxp[mxt-1]+1,T,w[T]); 48 while(mn[mnt]>w[T])Add(mnp[mnt-1]+1,mnp[mnt],mn[mnt]),mnt--; mn[++mnt]=w[T]; mnp[mnt]=T; Add(mnp[mnt-1]+1,T,-w[T]); 49 while(Q[_].r==T)T++,ans[Q[_].o]=ask(Q[_].l,T),_++,T--; 50 }for(int i=1;i<=q;++i)printf("%lld\n",ans[i]); 51 }
T2:Epidemic
大意:有$n$个$A_i$和$m$个$B_i$,在第$i$轮(从$0$开始),$A_{i \mod n}=B_{i \mod m}=A_{i \mod n} or\ B_{i \mod m}$。给出最开始为$1$的点不超过$2 \times 10^5$个。
求多少轮后所有的$A,B$均为$1$。$n,m \le 10^9$
首先非常明显的是,当$n,m$不互质的时候,可以把整个局面划分为$gcd$个子局面再合并答案,所以只考虑互质的情况即可。
首先有一个神仙结论:所有点被传染的方式(以$A$为例),传染它的$B$城市,要么最开始就是$1$,要么被一个最开始就是$1$的$A$类点
这样的话,我们考虑所有$A$类点是如何被传染的。
那么有关的点一共只有两种:初始就是$1$的,以及如果存在初始时$B_j=1$那么在$A_{j \mod n}$也可能是个传染源。
按照上面的传染规则,我们把所有的传染源排在左边,然后发现,对于所有节点编号$Id\ m-rn \equiv i$。
那么对于一个点编号为$x$的点一定是它在排列里的前驱传染的他,判一下各种边界就好了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 int g,n,m;ll ans,a,b; 5 vector<int>A[200005],B[200005]; 6 struct pt{int p,o;friend bool operator<(pt x,pt y){return (x.p<<1|x.o)<(y.p<<1|y.o);}}p[200005]; 7 int gcd(int x,int y){return y?gcd(y,x%y):x;} 8 void exgcd(ll&x,ll&y,int a,int b){ 9 if(!b){x=1;y=0;return;} 10 exgcd(x,y,b,a%b);int r=x;x=y;y=r-a/b*y; 11 } 12 int main(){ 13 cin>>n>>m; g=gcd(n,m); n/=g;m/=g; p[0].p=-1; 14 if(g>200000)return puts("-1"),0; 15 cin>>a; for(int i=1,x;i<=a;++i)scanf("%d",&x),A[x%g].push_back(x/g); 16 cin>>b; for(int i=1,x;i<=b;++i)scanf("%d",&x),B[x%g].push_back(x/g); 17 exgcd(a,b,n,m); a=(a%m+m)%m; b=(b%n+n)%n; 18 for(int _=0;_<g;++_){ 19 if(A[_].empty()&&B[_].empty())return puts("-1"),0; 20 int t=0; 21 for(int i=0;i<A[_].size();++i)p[++t]=(pt){A[_][i]*1ll*b%n,1}; 22 for(int i=0;i<B[_].size();++i)p[++t]=(pt){B[_][i]*1ll*b%n,0}; 23 sort(p+1,p+1+t); 24 p[t+1]=p[1];p[t+1].p+=n; 25 for(int i=1;i<=t;++i)if(p[i+1].p-p[i].p>1)ans=max(ans,((p[i+1].p-p[i].p-1ll)*m+(1ll*p[i].p*m%n))*g+_); 26 for(int i=1;i<=t;++i)if(p[i].p!=p[i+1].p&&p[i].o==0)ans=max(ans,(1ll*p[i].p*m%n)*g+_); 27 28 t=0; 29 for(int i=0;i<A[_].size();++i)p[++t]=(pt){A[_][i]*1ll*a%m,0}; 30 for(int i=0;i<B[_].size();++i)p[++t]=(pt){B[_][i]*1ll*a%m,1}; 31 sort(p+1,p+1+t); 32 p[t+1]=p[1];p[t+1].p+=m; 33 for(int i=1;i<=t;++i)if(p[i+1].p-p[i].p>1)ans=max(ans,((p[i+1].p-p[i].p-1ll)*n+(1ll*p[i].p*n%m))*g+_); 34 for(int i=1;i<=t;++i)if(p[i].p!=p[i+1].p&&p[i].o==0)ans=max(ans,(1ll*p[i].p*n%m)*g+_); 35 }cout<<ans<<endl; 36 }
T3:sort
大意:有一个长为$n$的有$k元素未知的排列。称一次操作为 选定偶数个下标然后两两配对然后交换其中的每对元素的位置。
设$f,g$表示把排列排成单位排列的最少步数以及这个步数下的操作方案数。对所有填充排列的方式求$f,g$的和。$n \le 10^5,k \le 12$
先考虑$k=0$
排列的题好像有几种常用的方法,这次是把所有元素看成边连起来,会形成若干环。
如果全是大小为$1$的环,那么步数为$0$方案为$1$
如果全是大小为$\le2$的环,那么步数是$1$方案是$1$
然后观察大样例之类的发现好像有$f = 2$。这的确成立。
手模一下发现,如果只有一个环,那么你只要选定其中一个元素在第一轮操作后的位置,那么其余元素就都能依次确定。所以方案数是$size$
如果只有两个等大的环,那么你第一轮操作可能会跨过两个环,发现这样的方案是$size$的,还要加上操作并不跨过环的$size^2$
对于更多等大的环,可以有一个$dp_{i,j}$表示大小为$i$的环有$j$个的方案数。$dp_{i,j}=dp_{i,j-1} \times i + dp_{i,j-2} \times i \times (j-1)$
对于不等大的环,发现两边如果有跨环交换则一定不合法,所以只需要对每种长度分别考虑最后答案相乘。
如果有未知元素那么相当于有若干环和$k$条链,你要拿这些链组成新的环,搜索就行了。
预处理上面的$dp$(只有$n\ ln\ n$和值)和逆元,然后每次对于一个新加的环除一下乘一下就好。
$O(n\ ln \ n +k \times Bell(k))$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 1000000007 4 #define S 100005 5 int F,G=1,a[S],n,k,cnt[S],al[S],d[S],L[13],lc,tot,X[S],Z[S]; 6 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 7 vector<int>dp[S],Iv[S]; 8 void sch(int x,int C){ 9 if(x==k+1){ 10 int r=1; 11 for(int i=1;i<=C;++i)for(int j=2;j<Z[i];++j)r*=j; 12 int g=G; 13 for(int i=1;i<=C;++i)x=X[i],g=1ll*g*dp[x][cnt[x]+1]%mod*Iv[x][cnt[x]]%mod,cnt[x]++; 14 if(cnt[1]==n)tot+=r; 15 else if(cnt[1]+(cnt[2]<<1)==n)F+=r,tot+=r; 16 else F+=2*r,tot=(tot+g*1ll*r)%mod; 17 for(int i=1;i<=C;++i)cnt[X[i]]--; 18 return; 19 } 20 for(int i=1;i<=C;++i)X[i]+=L[x],Z[i]++,sch(x+1,C),X[i]-=L[x],Z[i]--; 21 X[++C]=L[x];Z[C]=1;sch(x+1,C); 22 } 23 int main(){//freopen("sort5.in","r",stdin); 24 cin>>n>>k; 25 for(int i=1;i<=n;++i){ 26 dp[i].resize(n/i+1); Iv[i].resize(n/i+1); 27 dp[i][0]=Iv[i][0]=1; dp[i][1]=i; Iv[i][1]=qp(dp[i][1],mod-2); 28 for(int j=2;j<=n/i;++j)dp[i][j]=(dp[i][j-1]+dp[i][j-2]*(j-1ll))%mod*i%mod,Iv[i][j]=qp(dp[i][j],mod-2); 29 } 30 for(int i=1;i<=n;++i)scanf("%d",&a[i]); 31 for(int i=1;i<=n;++i)if(a[i])d[i]++,d[a[i]]++; 32 for(int i=1;i<=n;++i)if(d[i]==1&&a[i]){ 33 int l=0,p=i; 34 while(p)al[p]=1,p=a[p],l++; 35 L[++lc]=l; 36 } 37 for(;lc<=k;)L[++lc]=1; 38 for(int i=1;i<=n;++i)if(!al[i]&&d[i]==2){ 39 int l=0,p=i; 40 while(!al[p])al[p]=1,p=a[p],l++; 41 cnt[l]++; 42 } 43 for(int i=1;i<=n;++i)G=(G*1ll*dp[i][cnt[i]])%mod; 44 sch(1,0); 45 cout<<F<<endl<<tot<<endl; 46 }