T1
题目大意:从原点开始循环执行命令,问最后的位置
好吧这就是一道幼儿园的周期问题,模拟即可
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
int T,tt,n,xx,yy,lx,ly;
long long fx,fy;
char op[N];
int main() {
scanf("%s%d",op+1,&T);
n=strlen(op+1),tt=T/n;
for(int i=1;i<=n;i++) {
if(op[i]=='E')++xx;
else if(op[i]=='W')--xx;
else if(op[i]=='S')--yy;
else if(op[i]=='N')++yy;
}
for(int i=1;i<=(T%n);i++) {
if(op[i]=='E')++lx;
else if(op[i]=='W')--lx;
else if(op[i]=='S')--ly;
else if(op[i]=='N')++ly;
}
fx=1LL*xx*tt+lx;
fy=1LL*yy*tt+ly;
printf("%lld %lld",fx,fy);
}
T2
初始是 1 ,一次可以加 \(x\) 或加 \(y\) 或加 \(z\) ,问上限为 \(h\) 时最多能的到多少个数
\(40 \ \text{pts}\) :无脑的暴力
\(100 \ \text{pts}\) 设 \(D_i\) 为只用 \(y,z\) 的最小\(c\mod x=i\) ,所以
\(D_0=0\)
\(D_{(i+y)\mod x}=D_i+y\)
\(D_{(i+z)\mod x}=D_i+z\)
这样可以用最短路来转移。答案就是 \(\sum_{i=0}^{x-1}(\lfloor\dfrac{h-D_i}{x}\rfloor+1)*(n\ge D_i)\)
剩下的 \(h-D_i\) 都只用 \(x\) ,就有 \(\lfloor\dfrac{h-D_i}{x}\rfloor\) ,还可以不用 \(x\) ,所以加一
2021.3.15 19:46 发现这是一题经典的同余最短路
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005;
LL n,res,dis[N];
int x,y,z,cnt,l,r,q[N],vis[N];
int main() {
scanf("%lld%d%d%d",&n,&x,&y,&z),--n;
memset(dis,100,sizeof(dis));
dis[0]=0,q[r=1]=0;
for(int u,v;l<r;) {
u=q[++l],vis[u]=0;
if(dis[u]+y<dis[v=(u+y)%x])dis[v]=dis[u]+y,q[++r]=v;
if(dis[u]+z<dis[v=(u+z)%x])dis[v]=dis[u]+z,q[++r]=v;
}
for(int i=0;i<x;i++)
if(n>=dis[i])res+=(n-dis[i])/x+1;
printf("%lld",res);
}
T3
题意:维护一个序列,操作 1 把 \(L\le\forall i\le R\) 的元素加上 \(F_{i-L+1}\) 其中 \(F\) 表示斐波那契数列
良心的暴力分:\(40\),折腾一个小时线段树的结果
赛后:我*,懒标记可以用 vector !!!为什么不会超时:我也不知道
然后线段树巨大常数会超时,用分块。下传标记、修改、查询都是 \(O(\sqrt n)\) ,总 \(O(m\sqrt n)\)
#include<bits/stdc++.h>
#define RG register
using namespace std;
typedef long long LL;
const LL P=1000000009;
const int N=100005,SQ=1005;
inline int Rd() {
RG int x=0;
char C=getchar();
for(;C<'0'||C>'9';C=getchar()) ;
for(;C>'/'&&C<':';C=getchar()) x=(x<<1)+(x<<3)+(C^48);
return x;
}
LL x[N],sm[SQ],ans,f[N]={0,1,1},s[N]={0,1,2};
vector<int>tg[SQ];
int n,T,pos[N],L[SQ],R[SQ],bl;
inline void down(RG int p) {
if(tg[p].empty())return;
for(RG int i=0;i<tg[p].size();i++)
for(RG int j=L[p];j<=R[p];j++)
(x[j]+=f[j-tg[p][i]+1])%=P;
tg[p].clear();
}
inline void mdy(RG int l,RG int r) {
RG int p=pos[l],q=pos[r];
if(p==q) {
for(RG int i=l;i<=r;i++)
(x[i]+=f[i-l+1])%=P,
(sm[p]+=f[i-l+1])%=P;
return;
}
for(RG int i=l;i<=R[p];i++)
(x[i]+=f[i-l+1])%=P,
(sm[p]+=f[i-l+1])%=P;
for(RG int i=p+1;i<=q-1;i++)
(sm[i]+=(s[R[i]-l+1]-s[L[i]-l]+P)%P)%=P,
tg[i].push_back(l);
for(RG int i=L[q];i<=r;i++)
(x[i]+=f[i-l+1])%=P,
(sm[q]+=f[i-l+1])%=P;
}
inline void ask(RG int l,RG int r) {
RG int p=pos[l],q=pos[r];
if(p==q) {
down(p);
for(RG int i=l;i<=r;i++)(ans+=x[i])%=P;
return;
}
down(p),down(q);
for(RG int i=l;i<=R[p];i++)(ans+=x[i])%=P;
for(RG int i=p+1;i<=q-1;i++)(ans+=sm[i])%=P;
for(RG int i=L[q];i<=r;i++)(ans+=x[i])%=P;
}
int main() {
n=Rd(),T=Rd();
for(RG int i=3;i<=n;i++)
f[i]=(f[i-1]+f[i-2])%P,
s[i]=(s[i-1]+f[i])%P;
for(RG int i=1;i<=n;i++)x[i]=1LL*Rd();
bl=sqrt(1.0*n);
for(RG int i=1;i<=bl;i++)L[i]=R[i-1]+1,R[i]=i*bl;
if(R[bl]<n)++bl,L[bl]=R[bl-1]+1,R[bl]=n;
for(RG int i=1;i<=bl;i++)
for(RG int j=L[i];j<=R[i];j++)
pos[j]=i,(sm[i]+=x[j])%=P;
for(RG int opt,l,r;T--;) {
opt=Rd(),l=Rd(),r=Rd();
if(opt==1)mdy(l,r);
else ans=0,ask(l,r),printf("%lld\n",ans);
}
}
方法 2
维护一个贡献数组 \(d\) ,可以 \(O(1)\) 修改:
\(d_l\leftarrow d_l+1\)
\(d_{r+1}\leftarrow d_{r+1}-f_{r-l+2}\)
\(d_{r+2}\leftarrow d_{r+2}-f_{r-l+1}\)
重算:\(O(n)\) 计算 \(d\) ,即对原数组的贡献 \(d_i=d_{i-1}+d_{i-2}\)
但是这样复杂度依然不理想,发现瓶颈是重算
运用平衡规划(好高级的样子),每 \(k\) 次进行重算
发现询问时不一定刚刚重算完,所以这一段贡献单独算进答案
容易发现当 \(k=\sqrt m\) 时复杂度最优,为 \(O((n+m)\sqrt m)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL P=1000000009;
const int N=100005;
int n,T,bl,ls=1,opt[N],l[N],r[N];
LL res,x[N],sm[N],d[N],f[N]={0,1,1},s[N]={0,1,2};
int main() {
scanf("%d%d",&n,&T);
for(int i=3;i<=n;i++) {
f[i]=(f[i-1]+f[i-2])%P;
s[i]=(s[i-1]+f[i])%P;
}
for(int i=1;i<=n;i++) {
scanf("%d",&x[i]);
sm[i]=(sm[i-1]+x[i])%P;
}
bl=sqrt(1.0*T);
for(int i=1;i<=T;i++) {
scanf("%d%d%d",&opt[i],&l[i],&r[i]);
if(opt[i]==1) {
++d[l[i]];
d[r[i]+1]-=f[r[i]-l[i]+2];
d[r[i]+2]-=f[r[i]-l[i]+1];
} else {
res=(sm[r[i]]-sm[l[i]-1]+P)%P;
for(int j=ls;j<=i;j++)
if(opt[j]==1 && r[j]>=l[i] && l[j]<=r[i])
res=(res+s[min(r[i],r[j])-l[j]+1]-s[max(l[i],l[j])-l[j]]+P)%P;
printf("%lld\n",res);
}
if(i%bl==0) {
sm[1]=(x[1]+=d[1]),ls=i+1;
for(int j=2;j<=n;j++) {
(d[j]+=d[j-1]+d[j-2]+P)%=P;
(x[j]+=d[j]+P)%=P;
sm[j]=(sm[j-1]+x[j])%P;
}
memset(d,0,sizeof(d));
}
}
}
T4
赛时:怎么又是期望 dp ,放弃了
赛后:三行代码认真的吗?
其实就是把相邻两个数的较大值取倒数,然后求和
#include<bits/stdc++.h>
using namespace std;
const int N=10000005;
int n,A,B,C,x[N];
double s;
int main() {
scanf("%d%d%d%d%d",&n,&A,&B,&C,x+1);
for(int i=2;i<=n;i++)
x[i]=(1LL*x[i-1]*A+1LL*B)%100000001;
for(int i=1;i<=n;i++)x[i]=x[i]%C+1;
for(int i=1;i<=n;i++)s+=1.0/max(x[i],x[i%n+1]);
printf("%.3lf",s);
}
总结
T2:了解了同余最短路,希望下次知道
T3:原来懒标记可以这样+奇妙的差分+平衡规划
T4:不要以为期望 dp 很难