0228模拟赛(兔)
文明种植计划
题意
\(~~~~\) 一个排列初始时 \(p_i=i\),给出 \(n\) 个数的目标位置(\(n\) 为偶数),每次允许交换满足 \(2|i-j|\geq n\) 的两个位置 \(i,j\) 上的数。构造一组在 \(M\leq 3\times 10^5\) 以内的方案。
\(~~~~\) \(1\leq n\leq 3\times 10^5\).
题解
\(~~~~\) 原题 CF1148C 限制是 \(5n\),随便搞。
\(~~~~\) 这题我们考虑一种在三步之内交换两个东西的方法,具体来说如果我们想把 \(y\) 换到 \(x\):
\(~~~~\) 如果两个能直接互换那直接换即可,否则:
\(~~~~\) 若 \(x,y\) 同在某一侧,那就利用另一侧的端点来换。若在异侧,那么把 \(y\) 先换到对侧的端点,交换 \(1\) 和 \(n\) 再换到 \(x\) 即可。
\(~~~~\) 可以发现把每个数换到一个位置用 \(3\) 步及以内,那就直接把每个数换到目标位置即可。当然因为 \(1\) 和 \(n\) 在过程中会被打乱,所以 \(1\) 和 \(n\) 要最后来放。
代码
查看代码
// LUOGU_RID: 103248552
#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
vector<PII>Ans;
int n,Aim[300005],P[300005],pos[300005],To[300005];
inline int Abs(int x){return x>=0?x:-x;}
void Swap(int x,int y)
{
if(2*Abs(x-y)>=n) Ans.push_back(mp(x,y)),swap(P[x],P[y]),pos[P[x]]=x,pos[P[y]]=y;
else
{
if(x<=n/2&&y<=n/2)
{
Ans.push_back(mp(x,n));
Ans.push_back(mp(n,y));
Ans.push_back(mp(x,n));
swap(P[x],P[y]); pos[P[x]]=x; pos[P[y]]=y;
}
else if(x>n/2&&y>n/2)
{
Ans.push_back(mp(x,1));
Ans.push_back(mp(1,y));
Ans.push_back(mp(x,1));
swap(P[x],P[y]); pos[P[x]]=x; pos[P[y]]=y;
}
else if(x<=n/2&&y>n/2)
{
Ans.push_back(mp(y,1));
Ans.push_back(mp(1,n));
Ans.push_back(mp(n,x));
swap(P[y],P[1]);swap(P[1],P[n]);swap(P[n],P[x]);
pos[P[x]]=x; pos[P[n]]=n; pos[P[1]]=1; pos[P[y]]=y;
}
else
{
Ans.push_back(mp(x,n));
Ans.push_back(mp(1,n));
Ans.push_back(mp(n,y));
swap(P[x],P[n]); swap(P[1],P[n]); swap(P[n],P[y]);
pos[P[x]]=x; pos[P[n]]=n; pos[P[1]]=1; pos[P[y]]=y;
}
}
}
int main() {
// freopen("project.in","r",stdin);
// freopen("project.out","w",stdout);
read(n);
for(int i=1,x;i<=n;i++) read(x),Aim[x]=i,To[i]=x;
for(int i=1;i<=n;i++) P[i]=i,pos[i]=i;
for(int i=2;i<n;i++)
{
if(Aim[i]!=P[i])
Swap(i,pos[Aim[i]]);
}
if(Aim[1]!=P[1]) Swap(pos[Aim[1]],1);
if(Aim[n]!=P[n]) Swap(pos[Aim[n]],n);
printf("%d\n",(int)Ans.size());
for(int i=0;i<(int)Ans.size();i++) printf("%d %d\n",Ans[i].first,Ans[i].second);
return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。
*/
创世神的引导
\(~~~~\) 是一道捐出去的原创题,所以应出题人要求不写。
闯入的观测者
题意
\(~~~~\) 共 \(n\) 行的三角形,第 \(i\) 行有 \(i\) 个数。从 \((1,1)\) 开始画折线,每次 \((i,j)\) 向 \((i+1,j)\) 或 \((i+1,j+1)\) 画折线,要求共 \(m\) 条折线,每条折线都不在上一条折线的左侧。且有 \(k\) 个限制 \((x,y,z)\),表示第 \(x\) 条折线在 \((y,c)\) 画折线必须向 \((y+1,c+z)\)。
\(~~~~\) \(1\leq n,m\leq 20\).
题解
\(~~~~\) 考虑 \(dp_{i,S}\) 表示第 \(i\) 条折线,状态为 \(S\),那么直接转移复杂度 \(\mathcal{O(4^n \text{Poly}(n))}\),显然过不了。
\(~~~~\) 我们一格一格来,这样是 \(\mathcal{O(nm)}\) 的状态阶段,但是可以 \(\mathcal{O(1)}\) 转移过来。(类似轮廓线dp)但是怎么判左侧这个限制呢?还得加一维来记录上一条线在这里的 \(j\)。这样是 \(\mathcal{O(2^nn^2m)}\) 的,看起来很接近了,再优化一点点。
\(~~~~\) 我们发现实质是上一条线的 \(\operatorname{popcount}\) 前缀和时刻小于等于这条线的 \(\operatorname{popcount}\) 前缀和,那我们就不记录上一条线在这里的位置,而是改为如果在某个位置走了 \(1\) 那就把剩下的限制放宽即可,也即减去 \(\operatorname{lowbit}\).
代码
查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T sig=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')sig=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=sig;
}
const int MOD=1e9+7;
inline int Add(int a,int b){return (a+b)%MOD;}
inline int Dec(int a,int b){return (a-b+MOD)%MOD;}
inline int Mul(int a,int b){return 1ll*a*b%MOD;}
int f[25][25],dp[25][1048580];
inline int lowbit(int x){return x&(-x);}
int main() {
memset(f,-1,sizeof(f));
int n,m,q;
read(n);read(m);read(q);
for(int i=1,x,y,z;i<=q;i++) read(x),read(y),read(z),f[x][y]=z;
dp[0][0]=1;int Now=0;
for(int i=1;i<=m;i++)
{
for(int qwq=1,Tmp=1,now=(1<<(n-1))-1;qwq<n;qwq++,now-=Tmp,Tmp<<=1)
{
if(f[i][qwq]==0)
{
Now++; memset(dp[Now&1],0,sizeof(dp[Now&1]));
for(int k=0;k<(1<<(n-1));k++)
if(!(k&Tmp)) dp[Now&1][k]=Add(dp[Now&1][k],dp[!(Now&1)][k]);
}
if(f[i][qwq]==1)
{
Now++; memset(dp[Now&1],0,sizeof(dp[Now&1]));
for(int k=0;k<(1<<(n-1));k++)
dp[Now&1][k-lowbit(k&now)+Tmp]=Add(dp[Now&1][k-lowbit(k&now)+Tmp],dp[!(Now&1)][k]);
}
if(f[i][qwq]==-1)
{
Now++; memset(dp[Now&1],0,sizeof(dp[Now&1]));
for(int k=0;k<(1<<(n-1));k++)
{
if(!(k&Tmp)) dp[Now&1][k]=Add(dp[Now&1][k],dp[!(Now&1)][k]);
dp[Now&1][k-lowbit(k&now)+Tmp]=Add(dp[Now&1][k-lowbit(k&now)+Tmp],dp[!(Now&1)][k]);
}
}
}
}
int Ans=0;
for(int i=0;i<(1<<(n-1));i++) Ans=Add(Ans,dp[Now&1][i]);
printf("%d",Ans);
return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。
*/
岁月史书
【假题已删除】