这套题的考察思路其实不错。
三暴力,挂一个。
$T1$是送分,但是一眼觉得是大数据结构,于是直接扔掉了。
结果竟然是个原题。。。$dy$讲的只记住了只言片语思路早就忘了。。。
好歹这次会了。
$T2$的话写了个乱搞,结果把本来到手的$20$给写死循环了,加了句判断就$50$了。
$T3$差不多想到正解了,但是毒瘤出题人不给部分分于是我就白想了那么多拿了个暴力分走了。
为什么不给部分分啊啊啊啊啊只留下了$20$和正解完全没关系。
鬼知道他怎么想的。。。
T1:老夫
大意:有$n$人有参数$a_i,b_i$,若$c le b_i$则带来$cw$收益,否则若$p le a_i$则带来$p$收益。对$c=[1,max(b)+1]$的每个值时求最大收益。$n,a_i,b_i le 10^5$
把人排序后发现每次加入人就是区间加等差数列。
分块,区间加懒标记,维护单调队列(凸包)单调指针扫它就行了。
时间复杂度$O(n sqrt{b_i})$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 100605 4 struct ps{int a,b;friend bool operator<(ps x,ps y){return x.b<y.b;}}p[S]; 5 int n,w,mxb,t[11111],pt[11111],c[11111],q[11111][166];long long ans,mx,v[S]; 6 long long cal(int z,int o){return v[q[z][o]]+1ll*q[z][o]*t[z];} 7 int main(){ 8 cin>>n>>w; 9 for(int i=1;i<=n;++i)scanf("%d%d",&p[i].a,&p[i].b),mxb=max(mxb,p[i].b); 10 for(int i=1;i<=100000;++i)q[i/100][++c[i/100]]=i; 11 for(int i=1;i<=10000;++i)pt[i]=1; 12 sort(p+1,p+1+n); 13 int ptr=1; p[n+1].b=mxb+1; 14 for(int i=1;i<=mxb+1;++i){ 15 while(p[ptr].b<i){int x=p[ptr].a; 16 for(int i=x/100-1;~i;--i){ 17 t[i]++; 18 while(pt[i]!=c[i]&&cal(i,pt[i])<=cal(i,pt[i]+1))pt[i]++; 19 mx=max(mx,cal(i,pt[i])); 20 } 21 int z=x/100; 22 for(int i=z*100;i<=x;++i)v[i]+=i; 23 for(int i=z*100;i<z*100+100;++i)v[i]+=1ll*t[z]*i,mx=max(mx,v[i]); 24 t[z]=c[z]=0;pt[z]=1; 25 for(int i=z*100;i<z*100+100;++i){ 26 while(c[z]&&v[q[z][c[z]]]<=v[i])c[z]--; 27 while(c[z]>1&&(v[q[z][c[z]-1]]-v[q[z][c[z]]])*(i-q[z][c[z]])>(q[z][c[z]-1]-q[z][c[z]])*(v[i]-v[q[z][c[z]]]))c[z]--; 28 q[z][++c[z]]=i; 29 } 30 ans-=1ll*p[ptr].b*w;ptr++; 31 }ans+=(n-ptr+1ll)*w; printf("%lld ",ans+mx); 32 } 33 }
T2:打算
大意:构造一个循环节长度为$L$的$ULDR$序列。满足$n$条限制形如$t_i$时刻在位置$(x_i,y_i)$。$n le 10^5,L le 10^6$
首先麻烦的是二维坐标互相影响,如何解决?
很久之前就惊了一次的:旋转坐标系。这样之后每一步横纵坐标都要$+1/-1$,然后每一对$(+1/-1)(+1/-1)$都能恰好确定原坐标系中的一个方向。
问题转化成:每一步$+1$或$-1$.有若干限制形如在时刻t你要在位置$x$上.两维分别计算最后合并即可。
然后我们对于每一条限制拆成整循环节部分以及零散部分。然后列出若干等式之后可以得到一个整循环节位移的可行区间。
取一个合法值然后模拟构造方案即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 2222222 4 #define fail {puts("NO");exit(0);} 5 int n,L;long long x[S],y[S],t[S]; 6 struct ps{long long t,a,b;friend bool operator<(ps x,ps y){return x.t<y.t;}}p[S]; 7 void solve(long long*a){ 8 long long l=-1e18,r=1e18; 9 for(int i=1;i<=n;++i)p[i]=(ps){t[i]%L,t[i]/L,a[i]}; 10 p[n+1]=(ps){L,-1,0}; sort(p+1,p+1+n); 11 for(int i=1;i<=n+1;++i){ 12 long long dt=p[i].t-p[i-1].t,da=p[i].a-p[i-1].a,db=p[i].b-p[i-1].b; 13 if(!da)if(abs(db)>dt)fail else continue; 14 long double A=(0.+db-dt)/da,B=(0.+db+dt)/da; 15 if(A>B)swap(A,B); 16 l=max(1.L*l,ceil(A));r=min(1.L*r,floor(B)); 17 }if(l+L&1)l++;if(r+L&1)r--;if(l>r)fail; 18 for(int i=1;i<=n+1;++i){ 19 p[i].b-=p[i].a*l;int c=p[i-1].t,db=p[i].b-p[i-1].b; 20 while(db>0)a[c]=1,c++,db--; 21 while(db<0)a[c]=0,c++,db++; 22 while(c+2<=p[i].t)a[c]=1,c++,c[a]=0,c++; 23 if(c!=p[i].t)fail; 24 } 25 } 26 int main(){ 27 cin>>n>>L; 28 for(int i=1;i<=n;++i){ 29 scanf("%lld%lld%lld",&t[i],&x[i],&y[i]); 30 if(x[i]+y[i]+t[i]&1)fail; 31 x[i]+=y[i];y[i]<<=1;y[i]=x[i]-y[i]; 32 }solve(x);solve(y); 33 for(int i=0;i<L;++i)putchar(x[i]?(y[i]?'R':'U'):(y[i]?'D':'L')); 34 }
T3:报复社会
大意:问长度为$n$字符集大小为$3$的满足所有子串中任意两种字符出现次数差不超过$k$的字符串数。$n le 10^18,k le 5$
老思路,我们差分,那么设$f(a)$表示$a$的出现次数,我们的状态只与$f(a)-f(b),f(b)-f(c),f(a)-f(c)$有关。
发现只要过程中$f(a)-f(b)$的波动范围不超过$k$即可。于是我们枚举其波动范围的左端点$l1,l2,l3$。$O((k+1)^3)$
转移采用矩阵快速幂。我们只记录其中两个状态就知道了第三个状态,根据你枚举的区间左端点判一下是否合法即可。这复杂度是$O((k+1)^6logn)$
总复杂度是$O((k+1)^9logn)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 998244353 4 #define cl(s) memset(s,0,sizeof s) 5 long long n,c[66][66];int k,ans,o[7][7],oc,b[66][66],a[66]; 6 void mulb(){ 7 for(int i=1;i<=oc;++i)for(int j=1;j<=oc;++j)for(int l=1;l<=oc;++l)c[i][j]+=1ll*b[i][l]*b[l][j]; 8 for(int i=1;i<=oc;++i)for(int j=1;j<=oc;++j)b[i][j]=c[i][j]%mod,c[i][j]=0; 9 } 10 void mula(){ 11 for(int i=1;i<=oc;++i)for(int j=1;j<=oc;++j)c[0][i]+=1ll*a[j]*b[j][i]; 12 for(int i=1;i<=oc;++i)a[i]=c[0][i]%mod,c[0][i]=0; 13 } 14 int solve(int l1,int u1,int l2,int u2,int l3,int u3){ 15 cl(a);cl(b);cl(o);oc=0; 16 for(int i=0;i<=u1;++i)for(int j=0;j<=u2;++j)o[i][j]=(l3<=l1+l2+i+j-k&&l1+l2+i+j-k<=l3+u3)?++oc:0; 17 a[o[k-l1][k-l2]]=1; 18 for(int i=0;i<=u1;++i)for(int j=0;j<=u2;++j)if(o[i][j]){ 19 if(i)b[o[i][j]][o[i-1][j]]=1; 20 if(j)b[o[i][j]][o[i+1][j-1]]=1; 21 b[o[i][j]][o[i][j+1]]=1; 22 } 23 for(long long i=1;i<=n;i<<=1,mulb())if(n&i)mula(); 24 int s=0;for(int i=1;i<=oc;++i)s=(s+a[i])%mod;return s; 25 } 26 int main(){ 27 cin>>n>>k; 28 for(int i=1;i<=k;++i)for(int j=0;j<=k;++j)for(int l=0;l<=k;++l)ans=(ans+mod-solve(i,k-1,j,k,l,k)*3ll)%mod; 29 for(int i=1;i<=k;++i)for(int j=1;j<=k;++j)for(int l=0;l<=k;++l)ans=(ans+solve(i,k-1,j,k-1,l,k)*3ll)%mod; 30 for(int i=1;i<=k;++i)for(int j=1;j<=k;++j)for(int l=1;l<=k;++l)ans=(ans+mod-solve(i,k-1,j,k-1,l,k-1))%mod; 31 for(int i=0;i<=k;++i)for(int j=0;j<=k;++j)for(int l=0;l<=k;++l)ans=(ans+solve(i,k,j,k,l,k))%mod; 32 cout<<ans; 33 }
然后可以发现,当$l1+l2-l3$的值相同时,转移矩阵是完全相同的,只有初值不一样。
所以记录下来每次直接用,复杂度是$O((k+1)^7logn)$的。