2022-7-22考试
红绿灯
题目叙述
一个人上学,红灯持续 \(r\) 秒,绿灯持续 \(g\) 秒。先绿再红构成 grgrgr... 这样的周期。
上学他要从家出发经过 \(n-1\) 个十字路口,相邻两个地方的距离是 \(d_i\) 。\(d_0\sim d_{n+1}\),\(d_0\) 表示家距离第一个十字路口的距离,\(d_{n+1}\) 是最后一个十字路口走到学校的距离。
题解
考虑如果他被红灯堵住了,可以发现剩下的情况只和现在走到了哪个十字路口相关。
因此,要先预处理出来 \(f_i\) 表示从 \(i\) 出发走到最后的要走多久。
有两个问题,第一个是怎么找到第一个红灯,第二个是怎么预处理出所有 \(f_i\) 。
可以发现,这两个问题其实都是找到某一个位置后的第一个红灯,而红灯的本质是 \(\pmod (g+r)\ge g\) 的意思。
因此直接使用一棵动态开点线段树,维护是 \(\pmod (g+r)=i\) 时最早在什么时候达到这个状况。
另外,有个大坑!!!如果一开始就在红灯的时间范围内,那么不需要等红绿灯!!!
总结
- 注意题目隐含条件。在家不需要等红绿灯。
- 提出问题,解决问题即可。
代码
#include <cstdio>
#include <iostream>
#include <cstdlib>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(LL i=(l),i##ADJK=(r);i<=i##ADJK;++i)
#define ROF(i,r,l) for(LL i=(r),i##ADJK=(l);i>=i##ADJK;--i)
using namespace std;
typedef long long LL;
template<typename T>bool chkmax(T &x,const T y){return (x<y)?(x=y,1):0;}
template<typename T>bool chkmin(T &x,const T y){return (x>y)?(x=y,1):0;}
const LL MN=5e4+5;
LL N,g,r,d[MN];
LL minv[MN*32],ls[MN*32],rs[MN*32];
void upd(LL o){
minv[o]=1e9;
if(ls[o])chkmin(minv[o],minv[ls[o]]);
if(rs[o])chkmin(minv[o],minv[rs[o]]);
}
LL totnode;
void insert(LL &o,LL l,LL r,LL p,LL v){
if(!o)o=++totnode;
if(l==r)return minv[o]=v,void();
LL mid=(l+r)>>1;
if(p<=mid)insert(ls[o],l,mid,p,v);
else insert(rs[o],mid+1,r,p,v);
upd(o);
}
const LL inf=1e9;
LL get_min(LL o,LL l,LL r,LL ql,LL qr){
if(!o)return inf;
if(l>qr||r<ql)return inf;
if(ql<=l&&r<=qr)return minv[o];
LL mid=(l+r)>>1;
return min(get_min(ls[o],l,mid,ql,qr),get_min(rs[o],mid+1,r,ql,qr));
}
LL rt;
LL go[MN],pre[MN];
int main(){
freopen("light.in","r",stdin);
freopen("light.out","w",stdout);
scanf("%lld%lld%lld",&N,&g,&r);
FOR(i,1,N+1)scanf("%lld",&d[i]);
FOR(i,1,N+1)pre[i]=pre[i-1]+d[i];
LL sum=pre[N+1];
FOR(i,1,N+1)pre[i]%=(g+r);
go[N+1]=0;
ROF(i,N+1,1){
insert(rt,0,g+r-1,pre[i],i);
LL tmp=min(get_min(rt,0,g+r-1,g+pre[i-1],g+r-1),get_min(rt,0,g+r-1,pre[i-1]-r,pre[i-1]-1));
// 计算 go[i-1]
if(tmp==inf)go[i-1]=0;
else{
if(tmp==N+1) go[i-1]=0;
else go[i-1]=go[tmp]+(g+r)-(pre[tmp]-pre[i-1]+g+r)%(g+r);
}
}
LL Q=0;scanf("%lld",&Q);
while(Q--){
LL t=0;scanf("%lld",&t);
LL ans=t+sum;
LL tmp=t%(g+r);
if(tmp)tmp=(g+r)-tmp;
LL post=min(get_min(rt,0,g+r-1,g+tmp,g+r-1),get_min(rt,0,g+r-1,tmp-r,tmp-1));
if(post==inf)printf("%lld\n",ans);
else{
if(post!=N+1)printf("%lld\n",ans+go[post]+(g+r)-(pre[post]+t%(g+r))%(g+r));
else printf("%lld\n",ans);
}
}
// system("grep VmPeak /proc/$PPID/status >/dev/tty");
fclose(stdin);
fclose(stdout);
return 0;
}
全排列
题目叙述
两个长度为 \(m\) 的互不相同的数组成的序列,定义他们相似当且仅当任意两个位置的大小关系相同。
求两个1到n的全排列,逆序对数 \(\le E\) 并且相似的区间数量,在两个排列取遍 \(1\sim n\) 的所有排列的情况下的数量和。
题解
设 \(f_{i,j}\) 表示长度为 \(i\) 的排列,逆序对数量为 \(j\) 的方案数,前缀和一下就是逆序对数 \(\le j\) 的方案数。这题的答案相当于 \(\sum_{l=1}^n(\frac{n!}{l!})^2f_{l,e}\) ,这是枚举相似区间长度的意思。
代码
#include <cstdio>
#include <iostream>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(int i=(l),i##ADJK=(r);i<=i##ADJK;++i)
#define ROF(i,r,l) for(int i=(r),i##ADJK=(l);i>=i##ADJK;--i)
using namespace std;
typedef long long LL;
const int Mod=1e9+7,MN=505;
int ad(int x,int y){return ((x+y)>=Mod)?(x+y-Mod):(x+y);}
int dc(int x,int y){return ((x-y)<0)?(x-y+Mod):(x-y);}
int ml(int x,int y){return (LL)x*y%Mod;}
int add(int &x,int y){return ((x+=y)>=Mod)?(x-=Mod):x;}
int dec(int &x,int y){return ((x-=y)<0)?(x+=Mod):x;}
int ksm(int x,int y){
int ret=1;
for(;y;y>>=1,x=ml(x,x))if(y&1)ret=ml(ret,x);
return ret;
}
int T,n,E,f[MN][500*499/2+10],sum[500*499/2+10];
int get(int l,int r){
if(l>r)return 0;
l=max(l,0);
if(l==0)return sum[r];
else return dc(sum[r],sum[l-1]);
}
int fac[MN],caf[MN];
void init(){
f[1][0]=1;
FOR(i,2,500){
sum[0]=f[i-1][0];
FOR(j,1,i*(i-1)/2)sum[j]=ad(sum[j-1],f[i-1][j]);
FOR(j,0,i*(i-1)/2)f[i][j]=get(j-i+1,j);
}
FOR(i,1,500)FOR(j,1,i*(i-1)/2)add(f[i][j],f[i][j-1]);
// FOR(i,1,5){
// FOR(j,0,i*(i-1)/2)cerr<<f[i][j]<<" ";
// cerr<<endl;
// }
fac[0]=1;
FOR(i,1,500)fac[i]=ml(fac[i-1],i);
caf[500]=ksm(fac[500],Mod-2);
ROF(i,499,0)caf[i]=ml(caf[i+1],i+1);
}
int main(){
freopen("perm.in","r",stdin);
freopen("perm.out","w",stdout);
init();
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&E);
int ans=0;
FOR(l,1,n)add(ans,ml(n-l+1,ml((E<=l*(l-1)/2)?f[l][E]:f[l][l*(l-1)/2],ml(ml(caf[l],caf[l]),ml(fac[n],fac[n])))));
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}
点点井井
题目叙述
给定一些格子是 . 还是 # ,求一个点使得他到每个点的切比雪夫距离是偶数则这个点是 # ,否则是 . 。另外,要求在可行的情况下,到 (0,0) 的曼哈顿距离最小,在此基础上 x 坐标尽量大,如果还一样,y坐标尽量大。
题解
先切比雪夫距离转曼哈顿距离,然后就变成了要求 \(|x_i-a|+|y_i-b|\) 模 4 的余数。然后按照坐标将所有的格子划分为若干个部分(这些部分里面的东西对于其他的有东西的格子大小关系不变),对这些部分的左上右下左下右上一些地方暴力判断即可。复杂度 \(\mathcal O(n^2)\) 。

浙公网安备 33010602011771号