[考试反思]0501省选模拟85:低落

状态低迷持续中。

好困。垃圾$T2$题解又坑了我$3$小时。

$zzq$的题。题不错(难度貌似没有想象中的高),然而真的是用心出题用脚写题解用脚造数据。

$T1$写的$60$暴力$+20$部分分,忘了离散化直接把部分分挂没了。

$T2$想到了正解的一大半吧,但是优化方向错了最后也只有一个暴力。

然而其实只要再加一个看起来也很暴力但是复杂度正确的优化就可以$AC$了。。

$T3$思路想到了一半,但是不会证明正确性就丢掉了。

然而鬼知道为什么空白代码有$70$分?搜索$+return$就能$AC?$

后悔没交个空代码上去还能水个$rk1$

反正的确数据是水出天际了。

T1:rng

大意:有$n$个实数变量在$[l_i,r_i]$等概率取值。求序列期望逆序对数。$n le 10^5$

由期望线性性,我们可以依次考虑每一对变量对答案的贡献。

对于$l=0$的测试点,我们可以分类讨论:如果$i<j$

$r_i<r_j:ans+=frac{r_i^2}{2(r_i-l_i)(r_j-l_j)}$

$r_i ge r_j:ans+=frac{2r_ir_j-r_j^2}{2(r_i-l_i)(r_j-l_j)}$

所以我们可以维护树状数组,分别维护$frac{r_i^2}{2(r_i-l_i)},frac{2r_i}{2(r_i-l_i)},frac{-1}{2(r_i-l_i)}$的和。

然后对于每个$j$在两侧分别代入$frac{1}{r_j-l_j},frac{r_j}{r_j-l_j},frac{r_j^2}{r_j-l_j}$来查询

注意分母上都是$r-l$的形式。

然后对于$l eq 0$可以发现$f([l_i,r_i],f[l_j,r_j])=f([0,r_i],[0,r_j])-f([0,l_i],[0,r_j])-f([0,r_i],[0,l_j])+f([0,l_i],[0,l_j])$

要注意这里的$f([0,l_i],[0,r_j])$之类的在计算的时候,分母依旧是$r_i-l_i,r_j-l_j$而不能用$l_i,r_j$什么的。

所以只要带上$-1$的系数然后离散化搞一下就完事了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int mod=998244353,i2=499122177;
 4 #define S 200005
 5 map<int,int>M;
 6 int l[S],r[S],n,iv[S],ans,L,tv[S],Tc[S],Tv[S],z[S],vn;
 7 int cal(int l,int r){return (l+r)*(r-l+0ll)%mod*i2%mod;}
 8 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;}
 9 void add(int*t,int p,int v){p=M[p];for(;p<S;p+=p&-p)t[p]=(t[p]+v)%mod;}
10 int ask(int*t,int p,int a=0){p=M[p]-1;for(;p;p^=p&-p)a=(a+t[p])%mod;return a;}
11 void Add(int*t,int p,int v){p=M[p];for(;p;p^=p&-p)t[p]=(t[p]+v)%mod;}
12 int Ask(int*t,int p,int a=0){p=M[p];for(;p<S;p+=p&-p)a=(a+t[p])%mod;return a;}
13 int main(){
14     cin>>n;
15     for(int i=1;i<=n;++i)scanf("%d%d",&l[i],&r[i]),iv[i]=qp(r[i]-l[i],mod-2),z[++vn]=l[i],z[++vn]=r[i];
16     sort(z+1,z+1+vn);
17     for(int i=1;i<=vn;++i)M[z[i]]=i;
18     for(int i=1;i<=n;++i)
19         ans=(ans+(ask(tv,r[i])+Ask(Tv,r[i])*1ll*r[i]%mod*r[i]+Ask(Tc,r[i])*1ll*r[i])%mod*iv[i])%mod,
20         ans=(ans-(ask(tv,l[i])+Ask(Tv,l[i])*1ll*l[i]%mod*l[i]+Ask(Tc,l[i])*1ll*l[i])%mod*iv[i])%mod,
21         add(tv,r[i],1ll*r[i]*i2%mod*r[i]%mod*iv[i]%mod),
22         Add(Tc,r[i],iv[i]*1ll*r[i]%mod),
23         Add(Tv,r[i],-i2*1ll*iv[i]%mod),
24         add(tv,l[i],-1ll*l[i]*i2%mod*l[i]%mod*iv[i]%mod),
25         Add(Tc,l[i],-iv[i]*1ll*l[i]%mod),
26         Add(Tv,l[i],i2*1ll*iv[i]%mod);
27     cout<<(ans+mod)%mod<<endl;
28 }
View Code

T2:lg

大意:$prodlimits_{a_1,a_2...a_n}^{a_i in [1,m]} lcm(a)^{gcd(a)},n le 10^8,m le 2 imes 10^5$

首先这个序列的$lcm$看起来不是很好办。用一个之前用过的类似思想,如果$lcm$中含有$p^e$那么

在$p,p^2,p^3,...,p^e$的倍数时个计数一次即可统计对答案。这题又刚好是$prod$且$m$不太大所以可以枚举$p^e$表示$lcm$的一个约数。

$prodlimits_{p^e} p^{sumlimits_{a} gcd(a) [exists i,p^e | a_i] }$

首先这个“存在“很不好处理,存在至少一项相当于所有情况减去一项都没有。

然后对这个$gcd$做一些经典的反演最后可以得到$prodlimits_{p^e} p^{ sumlimits_{x=1}^{m} f(p^e,x) varphi(x)}$

其中$f(p^e,x)$表示序列中存在至少一个数是$p^e$的倍数且$gcd$是$x$的倍数的方案数。

用所有$gcd$是$x$倍数的数列减去所有$gcd$是$x$倍数且不含任何$p^e$倍数的数的方案数就是满足两个条件的方案数。

$f(p^e,x)=(frac{m}{x})^n - (frac{m}{x} - frac{m}{lcm(x,p^e)})^n$

预处理$[1,m]$的$n$次幂的值。其余暴力计算可以达到$O(m^2)$。

$prodlimits_{p^e} p^{ sumlimits_{x=1}^{m} f(p^e,x) varphi(x)}$

继续考虑这个式子,我们讨论一下$x$中是否含有质因子$p$

如果不含有$p$,那么$x imes p^e le m$否则$f$中的那个$lcm$就会很大,导致$f=0$不用计算。

所以$x$的枚举上界是$frac{m}{p^e}$。对所有的$p^e$求和总复杂度略低于调和级数。

如果含有质因子$p$那么就枚举$m$以内所有$p$的倍数。这样对于每个$p$的复杂度都是$O(frac{m}{p})$的。

所以总复杂度差不多是$O(m ln m)$的。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 998244352
 4 #define S 200005
 5 int n,m,ans=1,pw[S],np[S],pc,P[S],phi[S];
 6 int qp(int b,int t,int mo=mod,int a=1){for(;t;t>>=1,b=1ll*b*b%mo)if(t&1)a=1ll*a*b%mo;return a;}
 7 int f(int p,int pe,int x){
 8     int X=x;while(X%p==0&&pe%p==0)X/=p,pe/=p;
 9     return (pw[m/x]-pw[m/x-m/x/pe]+mod)%mod;
10 }
11 int cal(int pe,int p,int e){
12     int ans=0;
13     for(int i=1;i*pe<=m;++i)if(i%p!=0)ans=(ans+1ll*phi[i]*f(p,pe,i))%mod;
14     for(int i=p;i<=m;i+=p)ans=(ans+phi[i]*1ll*f(p,pe,i))%mod;
15     return (ans+mod)%mod;
16 }
17 int main(){
18     cin>>n>>m;
19     for(int i=1;i<=m;++i)pw[i]=qp(i,n);
20     phi[1]=1;
21     for(int i=2;i<=m;++i){
22         if(!np[i])P[++pc]=i,phi[i]=i-1;
23         for(int j=1,x;(x=i*P[j])<=m;++j)
24             if(i%P[j])phi[x]=phi[i]*(P[j]-1),np[x]=1;
25             else {np[x]=1;phi[x]=phi[i]*P[j];break;}
26     }
27     for(int j=1,p;p=P[j];++j)for(long long pe=p,e=1;pe<=m;e++,pe*=p)ans=1ll*ans*qp(p,cal(pe,p,e),mod+1)%(mod+1);
28     cout<<ans<<endl;
29 }
View Code

T3:pm

大意:给定排列,每次可以花费$1$代价交换两相邻元素,最后有多少个位置$a_i eq i$代价就加多少。最小化代价并构造方案。$n le 2 imes 10^5$

首先我们如果操作了,一定是对于某个长度为$x$的段操作$x-1$次,使其中元素全部归为,总代价$-1$

一个满足条件的区间需要:下标为$[l,r]$的同时值域也是$[l,r]$,区间逆序对数是$r-l$

第一个条件等价于:$sumlimits_{i=l}^{r} a_i - i = 0,forall i in [l,r],a[i] le r$

第一个条件可以前缀和做差,用$map$维护上一次出现相同前缀和值的位置即可。

第二个条件随便弄个数据结构查最大值就行。

逆序对那个限制,可以发现$[l,r]$这段区间的值域也是连续的了,那么它与前$[1,l-1]$形成的逆序对就是那个区间中大于$r$的数的个数。主席树。

然后可以得到$[1,r]$逆序对数减去上面这部分减去$[1,l-1]$逆序对数=$[l,r]$逆序对数。就可以检验一个区间是否合法了。

我们只需要取最靠右的左端点即可,如果有一个右端点满足了第一条条件:

如果违背了第二个条件,那么一定不存在合法左端点。

如果违背了第三个条件,那么对于其它可能的左端点$l_0$,显然$[l_0,l-1][l,r]$这两个区间不会发生数字交换。

所以就这么一维护得到所有可行区间然后做个记录方案的$dp$就行了。$O(nlogn)$

构造的话,对于区间$[l,r]$每次取出区间中最小的元素把它移到左侧对应位置,每次逆序对会$-1$,自然是正确的。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 200005
 4 int n,a[S],ST[20][S],hb[S],rt[S],w[S<<6],lc[S<<6],rc[S<<6],pc,lp[S],t[S],pre[S],ip[S],ans[S],cnt;
 5 int Max(int l,int r){int b=hb[r-l+1];return max(ST[b][l],ST[b][r-(1<<b)+1]);}
 6 map<int,int>M;
 7 #define md (L+R>>1)
 8 void insert(int&p,int c,int x,int L=1,int R=n){
 9     w[p=++pc]=w[c]+1;  if(L==R)return;
10     if(x<=md)insert(lc[p],lc[c],x,L,md),rc[p]=rc[c];
11     else insert(rc[p],rc[c],x,md+1,R),lc[p]=lc[c];
12 }
13 int ask(int p,int l,int L=1,int R=n){
14     if(l<=L)return w[p];
15     return l<=md?ask(lc[p],l,L,md)+w[rc[p]]:ask(rc[p],l,md+1,R);
16 }
17 void add(int p){for(;p;p^=p&-p)t[p]++;}
18 int Ask(int p,int a=0){for(;p<=n;p+=p&-p)a+=t[p];return a;}
19 struct P{int v,p;friend bool operator<(P x,P y){return x.v<y.v;}}T[S],R,dp[S];
20 void add(int p,P x){for(;p<=n;p+=p&-p)if(T[p]<x)T[p]=x;}
21 P ASK(int p,P a=T[0]){for(;p;p^=p&-p)if(a<T[p])a=T[p];return a;}
22 void solve(int l,int r){for(int i=l;i<=r;++i)while(ip[i]!=i)ans[++cnt]=ip[i]-1,ip[a[ip[i]-1]]++,swap(a[ip[i]],a[ip[i]-1]),ip[i]--;}
23 int main(){
24     cin>>n;
25     for(int i=1;i<=n;++i)scanf("%d",&a[i]),ST[0][i]=a[i],insert(rt[i],rt[i-1],a[i]),pre[i]=Ask(a[i])+pre[i-1],add(a[i]),ip[a[i]]=i;
26     for(int i=0;i<=19;++i)for(int j=1<<i;j<1<<i+1&&j<=n;++j)hb[j]=i;
27     for(int i=1;i<=19;++i)for(int j=1;j+(1<<i)-1<=n;++j)ST[i][j]=max(ST[i-1][j],ST[i-1][j+(1<<i-1)]);
28     M[0]=1;
29     for(int i=1,tot=0;i<=n;++i){
30         tot+=a[i]-i; int x=M[tot]; M[tot]=i+1;
31         if(x&&Max(x,i)<=i&&pre[i]-pre[x-1]-ask(rt[x-1],i+1)*(i-x+1)==i-x)lp[i]=x;
32     }
33     for(int i=1;i<=n;++i)if(lp[i])dp[i]=ASK(lp[i]-1),dp[i].v++,add(i,(P){dp[i].v,i});
34     P mx=T[0];int pos;
35     for(int i=1;i<=n;++i)if(mx<dp[i])mx=dp[i],pos=i;
36     while(mx.v)solve(lp[pos],pos),pos=dp[pos].p,mx=dp[pos];
37     cout<<cnt<<endl;
38     for(int i=1;i<=cnt;++i)printf("%d ",ans[i]);
39 }
View Code
原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12815133.html