【考后总结】6 月西安 NOI 模拟赛 4
6.21 冲刺国赛模拟 22#
T1 跳跃#
不妨看作两只青蛙从相同起点出发且跳跃次数相同,设 为两只青蛙分别在 位置,且相差步数 。由于需要记录相邻位置对答案贡献,我们在要求必须严格按照升序对处理状态,也就是必须保证当前跳跃的一只青蛙落点在另一只青蛙更前面,且靠后的青蛙可以也可以一步跳到或跳过靠前的一只,也就是 。
考虑优化,设 ,如果已知 ,再结合 的特殊性质,就能计算出 ,不放修改状态为 表示靠前的青蛙在 位置,其中两只青蛙分别跳 或 次的最大答案。同时发现直接模 就是 ,而对于 的情况,发现这个时候 由 到 是去到了两个去过的地方且 的值也没改变,所以不用考虑。
再优化,注意到 的用处实际是作为 整体,所以把状态改成 后者表示 与 的差,这样转移即可。
同时存在 不变, 先跳到 然后再跳到后面的位置,因此第二维不能完全按下标顺序转移,需要把 的情况放最后。
点击查看代码
int n,A,B;
int c[maxn];
int f[maxn][maxw<<1];
vector<int> V;
int main(){
freopen("jump.in","r",stdin);
freopen("jump.out","w",stdout);
n=read(),A=read(),B=read();
for(int i=1;i<=n;++i) c[i]=read();
memset(f,~0x3f,sizeof(f));
for(int i=1;i<=B;++i) f[i][maxw]=0;
for(int i=1;i<=A;++i) f[i-A+B][-1+maxw]=((i-A+B)-i)*(c[i-A+B]^c[i]);
for(int i=1,j;i<=n;++i){
V.clear();
for(int k=-i/A-1;k<=i/A+1;++k){
//i-j=(ai-aj)A+(bi-bj)B<=B
j=i-(k*A%B+B)%B;
if(i==j){
V.push_back(k);
continue;
}
if(i+A<=n&&j+B>=i+A){
f[i+A][k+1+maxw]=max(f[i+A][k+1+maxw],f[i][k+maxw]+(i+A-i)*(c[i+A]^c[i]));
}
if(j+A<=n&&j+A>=i){
f[j+A][-k+1+maxw]=max(f[j+A][-k+1+maxw],f[i][k+maxw]+(j+A-i)*(c[j+A]^c[i]));
}
if(j+B<=n&&j+B>=i){
f[j+B][-k+maxw]=max(f[j+B][-k+maxw],f[i][k+maxw]+(j+B-i)*(c[j+B]^c[i]));
}
}
for(int k:V){
if(i+A<=n){
f[i+A][k+1+maxw]=max(f[i+A][k+1+maxw],f[i][k+maxw]+(i+A-i)*(c[i+A]^c[i]));
f[i+A][-k+1+maxw]=max(f[i+A][-k+1+maxw],f[i][k+maxw]+(i+A-i)*(c[i+A]^c[i]));
}
if(i+B<=n){
f[i+B][k+maxw]=max(f[i+B][k+maxw],f[i][k+maxw]+(i+B-i)*(c[i+B]^c[i]));
f[i+B][-k+maxw]=max(f[i+B][-k+maxw],f[i][k+maxw]+(i+B-i)*(c[i+B]^c[i]));
}
}
}
printf("%d\n",f[n][maxw]);
return 0;
}
6.22 冲刺国赛模拟 23#
T1 無言#
容易发现编号相对应的去填是不劣的,于是一个球以其到对应位置的距离为半径,两侧的所有坑都要先被填满,相当于是一个区间对单点连边,进行拓扑排序。优化使用线段树优化建图。
点击查看代码
int n;
int a[maxn],b[maxn];
struct edge{
int to,nxt;
}e[maxm];
int head[maxn*5],cnt;
int deg[maxn*5];
inline void add_edge(int u,int v){
e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;
++deg[v];
}
queue<int> q;
ll ans;
int tot;
int pos[maxn];
map<int,int> mp;
struct SegmentTree{
#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
void build(int rt,int l,int r){
tot=max(tot,rt);
if(l==r){
pos[l]=rt;
mp[rt]=l;
return;
}
add_edge(rt<<1,rt),add_edge(rt<<1|1,rt);
build(lson),build(rson);
}
void update(int rt,int l,int r,int pl,int pr,int p){
if(pl<=l&&r<=pr){
add_edge(rt,p);
return;
}
if(pl<=mid) update(lson,pl,pr,p);
if(pr>mid) update(rson,pl,pr,p);
}
#undef mid
#undef lson
#undef rson
}S;
int main(){
freopen("nameless.in","r",stdin);
freopen("nameless.out","w",stdout);
n=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=n;++i) b[i]=read();
S.build(1,1,n);
for(int i=1;i<=n;++i){
int dis=abs(a[i]-b[i]);
ans+=dis;
if(a[i]<b[i]){
//to left
int l=i+1,r=lower_bound(a+1,a+n+1,2*b[i]-a[i])-a-1;
if(r==i) continue;
S.update(1,1,n,l,r,pos[i]);
}
else{
//to right
int l=lower_bound(a+1,a+n+1,2*b[i]-a[i])-a,r=i-1;
if(l==i) continue;
S.update(1,1,n,l,r,pos[i]);
}
}
printf("%lld\n",ans);
for(int i=1;i<=tot;++i){
if(!deg[i]) q.push(i);
}
while(!q.empty()){
int u=q.front();
q.pop();
if(mp.count(u)) printf("%d ",mp[u]);
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
--deg[v];
if(!deg[v]) q.push(v);
}
}
printf("\n");
return 0;
}
T2 排列#
有一个 的限制,所以考虑倍增之类的做法,设 表示第 个排列中 置换到的数,那么可以以 为定义,如果是 的逆,那就是减去 ,所以我们进行 次操作得到的应该是 ,其中 ,这样是可以取到所有 的奇数的,由于答案对 取模,且 ,那么当 为奇数时,取模后可以取遍所有的数。
在此基础上考虑 是偶数,实际上从 到 在模意义下理论上最多只要增加或减少 就可以了,那么我们可以舍弃前两次置换来让 是偶数时成立。
注意到 是一定不行的,如果我们把 个相邻划为一组,那么需要奇偶性两个改变两个不变,同时需要其本身发生相对位置变化,从而求逆时变成另两个改变,这个时候 变为 即可,此时 奇偶性改变,且位置改变,这样求逆就是 奇偶性改变了,这就是 是 的倍数的情况。最后一种把最后 个划为一组,第一个排列对前 个做,第二个对后 个做就行了。
点击查看代码
int n,k;
int main(){
freopen("permutation.in","r",stdin);
freopen("permutation.out","w",stdout);
n=read();
if(n==1) return printf("1\n0\n"),0;
else if(n==2) return printf("-1\n"),0;
k=__lg(n-1)+2;
printf("%d\n",k);
if(n&1){
for(int i=1;i<=k;++i){
for(int j=0;j<n;++j){
printf("%d ",(j+(1<<i-1))%n);
}
printf("\n");
}
}
else if(n%4==0){
for(int j=0;j<n;++j){
if(j%4<=1) printf("%d ",j+2);
else if(j%4==2) printf("%d ",j-1);
else printf("%d ",j-3);
}
printf("\n");
for(int i=1;i<k;++i){
for(int j=0;j<n;++j){
printf("%d ",(j+(1<<i-1))%n);
}
printf("\n");
}
}
else{
for(int j=0;j<n-2;++j){
if(j%4<=1) printf("%d ",j+2);
else if(j%4==2) printf("%d ",j-1);
else printf("%d ",j-3);
}
printf("%d %d\n",n-2,n-1);
for(int j=0;j<n-4;++j) printf("%d ",j);
printf("%d %d %d %d\n",n-2,n-1,n-3,n-4);
for(int i=1;i<k-1;++i){
for(int j=0;j<n;++j){
printf("%d ",(j+(1<<i-1))%n);
}
printf("\n");
}
}
return 0;
}
作者:SoyTony
出处:https://www.cnblogs.com/SoyTony/p/Simulation_Problems_of_NOI_in_Xian_June_4.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效