[考试反思]0509省选模拟92:警示
同一种低错能犯不知道多少次。就应该有个人在我每次弱智的时候骂我一顿不然真是不长脑子。
$50$分$50$分的挂。。。(虽说牛更惨直接把绿框挂丢了
莫队排序$x.l/B<x.l/B$。$y$呢?
然后就直接$T$成暴力。亏得我还写了对拍,没有试大测试点。(暴力跑不出来)
之前就有一次因为对拍没有试大数据爆炸了,
但愿能长记性吧。。。
本来是拿下构造剩下暴力写满的。构造题有多解但是最开始没有$spj$我人都傻了
(最近构造题好多。貌似终于发现了第一个我比较擅长的题型。。。或许也不好说。。。菜死了
耽误了不少时间而且把心态弄得有点炸。。。$T1$那玩意都做烂了还没想出正解的确可能受到了一定影响
再说了。。。根号算法就那么几个咋每次都用不好。。。
反正状态不是很好。。。下次还要调整啊。。。
T1:Mansion
大意:有$n$个点,每个点有权值$a_i,b_i$。多次询问$\max\limits_{i=vl}^{vr} (\sum\limits_{j=l}^{r} b_j [a_j = i] ) $。$n,m,q \le 1.5 \times 10^5$
被牛嘲讽了。
是个类似于区间众数的问题。这种问题最低复杂度也就是$O(n^{1.5})$了
学过的根号就那么几种,所以不难想到莫队。维护线段树即可。复杂度是$O(\sqrt{n} log n)$的。
然后在莫队中,修改次数是$O(n\sqrt{n})$的但是查询次数是$O(n)$的,
所以根号平摊一下,分块$O(1)$修改$O(\sqrt{n})$查询就行了。
然而不支持删除。那就把莫队回滚一下。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int S=150005,B=400; 4 struct Qs{int l,r,vl,vr,o;friend bool operator<(Qs x,Qs y){return x.l/B!=y.l/B?x.l/B<y.l/B:x.r<y.r;}}Q[S]; 5 int n,m,q,a[S],b[S],p[S],t;long long w[S],c[S],ans[S],wv[S],cv[S]; 6 #define lc p<<1 7 #define rc p<<1|1 8 #define md (L+R>>1) 9 void add(int x,int v,int op){ 10 if(op)p[++t]=x,wv[t]=w[x],cv[t]=c[x/B]; 11 w[x]+=v; c[x/B]=max(c[x/B],w[x]); 12 } 13 long long ask(int l,int r,long long a=0){ 14 if(l/B==r/B)for(int i=l;i<=r;++i)a=max(a,w[i]); 15 else{ 16 for(int i=l;i<l/B*B+B;++i)a=max(a,w[i]); 17 for(int i=r;i>=r/B*B;--i)a=max(a,w[i]); 18 for(int i=l/B+1;i<r/B;++i)a=max(a,c[i]); 19 }return a; 20 } 21 void clear(){while(t)w[p[t]]=wv[t],c[p[t]/B]=cv[t],t--;} 22 int main(){//freopen("1.in","r",stdin);freopen("1.out","w",stdout); 23 scanf("%d%d%d",&n,&m,&q); 24 for(int i=1;i<=n;++i)scanf("%d",&a[i]); 25 for(int i=1;i<=n;++i)scanf("%d",&b[i]); 26 for(int i=1;i<=q;++i)scanf("%d%d%d%d",&Q[i].l,&Q[i].r,&Q[i].vl,&Q[i].vr),Q[i].o=i; 27 Q[0].l=-B; sort(Q+1,Q+1+q); 28 for(int l=1,r=0,i=1;i<=q;++i){ 29 if(Q[i].l/B!=Q[i-1].l/B){ 30 l=Q[i].l/B*B+B;r=l-1; 31 for(int i=1;i<=m;++i)w[i]=c[i]=0; 32 } 33 if(Q[i].l/B==Q[i].r/B){ 34 for(int j=Q[i].l;j<=Q[i].r;++j)add(a[j],b[j],1); 35 ans[Q[i].o]=ask(Q[i].vl,Q[i].vr); 36 clear(); continue; 37 } 38 while(r<Q[i].r)++r,add(a[r],b[r],0); 39 while(l>Q[i].l)--l,add(a[l],b[l],1); 40 ans[Q[i].o]=ask(Q[i].vl,Q[i].vr); 41 clear(); l=Q[i].l/B*B+B; 42 }for(int i=1;i<=q;++i)printf("%lld\n",ans[i]); 43 }
T2:Permutation
大意:给定一个陪列,每次操作可以选定一些元素,被选中的元素放到所有没选中的元素之后,且选中的和未选中的元素内部相对顺序不变。
要求最小化变为元排列的步数以及构造方案。$n \le 5 \times 10^4$
对于任意两个元素$a,b$,不妨设$a<b$
若在一开始$a$就在$b$前面,那么在每次操作中,两个数所经历的是否被选中的状态可以完全相同。
如果被选中是$1$不被选中是$0$,那么对于每个数经历的操作会形成一个$01$串,如$w_a$(注意是针对数说的,而不是针对下标)
若$a<b,ia<ib$则$w_a$可以等于$w_b$
然后考虑如果$w_a \neq w_b$有哪些可能。
如果操作序列不完全相同的话,最终两元素的相对位置由 最后一次两者选中状态不同的那次操作中谁被选中了 决定。
具体而言,如果最后一次不同是在操作$i$,则若$a<b$则$w_{a,i} = 0 ,w_{b,i}=1$
我们发现这样的话,我们把二进制串理解成二进制数的话(后面的操作为高位前面的操作是低位)
那么就是说,若$a<b$且$w_a \neq w_b$则$w_a < w_b$
整理得到对于任意$a<b$,若$p_a < p_b$则$w_a \le w_b$,否则$w_a < w_b$
这样我们就得到了$n^2$个限制关系。没法做。
考虑到谁在谁前面 这种关系是有传递性的,最后我们要构造的是元排列,那么只要$1$在$2$前面,$2$在$3$前面。。。$n-1$在$n$前面就足够了
不再需要$1$在$3$前面这种限制。那么现在就只剩下了$n-1$条限制。整理如下:
$0 \le w_1 <= w_2 <= w_3 <= ... <= w_n <2^{ans}$
其中的$<=$可能被替换为$<$或$\le$。为了最小化$ans$我们一定会在所有可以和前面一个值取等的地方取等,不能取等就只$+1$
显然是最优的。这样就得到了所有的$w$。$ans$和方案也就显然了。
1 #include<cstdio> 2 #define S 55555 3 int p[S],ip[S],n,ord,ans,v[S],op,q1[S],q2[S],t1,t2; 4 int main(){ 5 scanf("%d%d",&n,&op); 6 for(int i=1;i<=n;++i)scanf("%d",&p[i]),ip[p[i]]=i; 7 for(int i=n-1;i;--i)v[i]=ip[i]>ip[i+1]?++ord:ord; 8 while(1<<ans<=ord)ans++; 9 printf("%d\n",ans); 10 if(!op)return 0; 11 for(int i=1;i<=n;++i)v[i]=ord-v[i]; 12 for(int i=1;i<=n;++i)printf("%d ",p[i]);puts(""); 13 while(ans--){ 14 t1=t2=0; 15 for(int i=1;i<=n;++i)if(v[p[i]]&1)q1[++t1]=p[i],v[p[i]]>>=1; 16 else q2[++t2]=p[i],v[p[i]]>>=1; 17 for(int i=1;i<=t2;++i)printf("%d ",p[i]=q2[i]); 18 for(int i=1;i<=t1;++i)printf("%d ",p[i+t2]=q1[i]); puts(""); 19 } 20 }
T3:Grid Game
由于太菜咕掉了。去看牛神的博客吧。