2019.07.08【NOIP提高组】模拟 A 组

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 xxmodp,最少需要多少次,如果永远不能,输出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[xxmodp]+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 pxppyp
所以这是单调不下降的,那么就可以用双指针离线求出所有答案, 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]!=&#x27;e&#x27;)\\ 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]=minf[i1,j](j̸=s[i],s[i1]!=e)f[i1,s[i]]+2g[i1,s[i],j](j̸=s[i])g[i1,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]=minf[i1,j]+3(j̸=s[i])f[i1,s[i]]+5g[i1,j,k]+1(j̸=s[i],k̸=s[i])g[i1,s[i],k]+3(k̸=s[i])g[i1,j,s[i]]+3(j̸=s[i])g[i1,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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值