[考试反思]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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

posted @ 2020-04-24 22:17  DeepinC  阅读(172)  评论(0编辑  收藏  举报