JZOJ 3318 LOJ 2685 Brunhilda的生日
题目
给定 n n n个质数,有 m m m组询问,问对于一个数 x x x,可以用其中一个质数 p p p把它变为 x − x   m o d   p x-x\bmod p x−xmodp,最少需要多少次,如果永远不能,输出oo
分析
这显然是一个递推式,
a
n
s
[
x
]
=
a
n
s
[
x
−
x
 
m
o
d
 
p
]
+
1
ans[x]=ans[x-x\bmod p]+1
ans[x]=ans[x−xmodp]+1
可以发现当
x
>
y
x>y
x>y
那么
⌊
x
p
⌋
p
≥
⌊
y
p
⌋
p
\lfloor\frac{x}{p}\rfloor p\geq \lfloor\frac{y}{p}\rfloor p
⌊px⌋p≥⌊py⌋p
所以这是单调不下降的,那么就可以用双指针离线求出所有答案,
O
(
1
)
O(1)
O(1)回答
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=10000001;
int n,m,big[N],f[N],ans[N];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans==-1) {printf("oo"); return;}
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
signed main(){
n=iut(); m=iut();
for (rr int i=1;i<=n;++i){
rr int x=iut();
for (rr int j=0;j<N;j+=x) big[j]=big[j]>x?big[j]:x;
}
for (rr int i=1,j=0;i<N;f[i]=j,++i)
while (!big[j]||j+big[j]<=i) ++j;
for (rr int i=1;i<N;++i){
if (f[i]==i) {ans[i]=-1; continue;}
ans[i]=ans[f[i]]+(ans[f[i]]>=0);
}
for (rr int i=1;i<=m;++i) print(ans[iut()]),putchar(10);
return 0;
}
JZOJ 3319 LOJ 2686 雪地足迹
分析
一开始我以为只有两种答案,于是就拿了2分,然后发现我太naive了,然而这道题只是一道广搜,倒着推,把从起点开始的连通块全部标记,那么如果还有答案相邻的位置是有不同的动物,所以把它们全部加入队列后再次广搜
代码
#include <cstdio>
#define rr register
using namespace std;
const int N=16000000,dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int n,m,ans,head[2],tail[2],qx[2][N^1],qy[2][N^1];
char s[4001][4001]; bool v[4001][4001];
inline bool bfs(int now){
rr bool flag=0;
while (head[now]<=tail[now]){
rr int x=qx[now][head[now]],y=qy[now][head[now]]; ++head[now];
for (rr int i=0;i<4;++i){
rr int zx=x+dx[i],zy=y+dy[i];
if (zx<1||zx>n||zy<1||zy>m||v[zx][zy]||s[zx][zy]=='.') continue;
if (s[x][y]==s[zx][zy]) qx[now][++tail[now]]=zx,qy[now][tail[now]]=zy;
else qx[now^1][++tail[now^1]]=zx,qy[now^1][tail[now^1]]=zy,flag=1;
v[zx][zy]=1;
}
}
return flag;
}
signed main(){
scanf("%d%d",&n,&m); gets(s[0]+1);
for (rr int i=1;i<=n;++i) gets(s[i]+1);
v[1][1]=qx[0][++head[0]]=qy[0][++tail[0]]=1;
for (rr bool k=0;;k^=1){
++ans; head[k^1]=tail[k^1]=0;
if (!bfs(k)) break;
};
return !printf("%d",ans);
}
JZOJ 3320 LOJ 2687 Vim
分析
线头dp??
对于一个e,要hx才能删掉它,所以要用2*e的个数的操作
设
f
[
i
,
j
]
f[i,j]
f[i,j]为一条线跨过
i
i
i这个点,在其右边落点的字符为
j
j
j的最小代价;
设
g
[
I
,
j
,
k
]
g[I,j,k]
g[I,j,k]为第一次跨过
i
i
i点落在
j
j
j字符上,再折回左边,第二次跨到右边,落在
k
k
k字符上
的最小代价。
若当前点字母为
e
e
e,就直接拷贝上一个dp方程,否则
f
[
i
]
[
j
]
=
min
{
f
[
i
−
1
,
j
]
(
j
≠
s
[
i
]
,
s
[
i
−
1
]
!
=
′
e
′
)
f
[
i
−
1
,
s
[
i
]
]
+
2
g
[
i
−
1
,
s
[
i
]
,
j
]
(
j
≠
s
[
i
]
)
g
[
i
−
1
,
s
[
i
]
,
s
[
i
]
]
+
2
f[i][j]=\min\begin{cases} f[i-1,j](j≠s[i],s[i-1]!='e')\\ f[i-1,s[i]]+2\\ g[i-1,s[i],j] (j≠s[i])\\ g[i-1,s[i],s[i]]+2 \end{cases}
f[i][j]=min⎩⎪⎪⎪⎨⎪⎪⎪⎧f[i−1,j](j̸=s[i],s[i−1]!=′e′)f[i−1,s[i]]+2g[i−1,s[i],j](j̸=s[i])g[i−1,s[i],s[i]]+2
g
[
i
]
[
j
]
[
k
]
=
min
{
f
[
i
−
1
,
j
]
+
3
(
j
≠
s
[
i
]
)
f
[
i
−
1
,
s
[
i
]
]
+
5
g
[
i
−
1
,
j
,
k
]
+
1
(
j
≠
s
[
i
]
,
k
≠
s
[
i
]
)
g
[
i
−
1
,
s
[
i
]
,
k
]
+
3
(
k
≠
s
[
i
]
)
g
[
i
−
1
,
j
,
s
[
i
]
]
+
3
(
j
≠
s
[
i
]
)
g
[
i
−
1
,
s
[
i
]
,
s
[
i
]
]
+
5
g[i][j][k]=\min\begin{cases} f[i-1,j]+3 (j≠s[i]) \\ f[i-1,s[i]]+5\\ g[i-1,j,k]+1(j≠s[i],k ≠s[i])\\ g[i-1,s[i],k]+3 (k ≠s[i])\\ g[i-1,j,s[i]]+3(j ≠s[i])\\ g[i-1,s[i],s[i]]+5 \end{cases}
g[i][j][k]=min⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧f[i−1,j]+3(j̸=s[i])f[i−1,s[i]]+5g[i−1,j,k]+1(j̸=s[i],k̸=s[i])g[i−1,s[i],k]+3(k̸=s[i])g[i−1,j,s[i]]+3(j̸=s[i])g[i−1,s[i],s[i]]+5
但是这样还不够,要建立一个虚拟字母,但是这样答案要减去2,滚动一下空间就很小了
代码
#include <cstdio>
#include <cstring>
#define rr register
using namespace std;
int n,ans=-2,dp[2][11],f[2][11][11];
inline signed min(int a,int b){return a<b?a:b;}
signed main(){
scanf("%d\n",&n);
memset(dp[0],42,sizeof(dp[0])),memset(f[0],42,sizeof(f[0]));
rr int now=getchar()^96; dp[0][now]=0; rr bool need=0;
for (rr int i=1;i<=n;++i,need=now==5,now=getchar()^96)
if (now==5) memcpy(dp[i&1],dp[(i&1)^1],sizeof(dp[i&1])),
memcpy(f[i&1],f[(i&1)^1],sizeof(f[i&1])),ans+=2;
else{
rr int t=min(dp[(i&1)^1][now],f[(i&1)^1][now][now])+5;
for (rr int j=0;j<11;++j){
for (rr int k=0;k<11;++k){
f[i&1][j][k]=t;
if (k!=now) f[i&1][j][k]=min(f[i&1][j][k],f[(i&1)^1][now][k]+3);
if (j==now) continue;
rr int tt=min(dp[(i&1)^1][j],f[(i&1)^1][j][now])+3;
if (k!=now) tt=min(tt,f[(i&1)^1][j][k]+1);
f[i&1][j][k]=min(f[i&1][j][k],tt);
}
dp[i&1][j]=t-3;
if (j==now) continue;
dp[i&1][j]=min(dp[i&1][j],f[(i&1)^1][now][j]);
if (!need) dp[i&1][j]=min(dp[i&1][j],dp[(i&1)^1][j]);
}
}
return !printf("%d",dp[n&1][0]+ans);
}