2019.08.12【NOIP提高组】模拟 A 组

JZOJ 6293 迷宫

题目

在这里插入图片描述


分析

因为其没有后效性
动态dp,用线段树维护区间的最短路径,线段树维护一个dp方程,也就是该列的某行到某行的最短路径,那么区间判断最小值,注意特判


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
#define min(a,b) ((a)<(b)?(a):(b)) 
using namespace std;
const int N=200101; bool a[5][N],flag; int n,m,q;
struct maix{int p[5][5];}A[N<<2],ANS;
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 maix mul(maix A,maix B){
	rr maix C;
    memset(C.p,42,sizeof(C.p));
	for (rr int i=0;i<n;++i)
    for (rr int j=0;j<n;++j)
    for (rr int k=0;k<n;++k)
    C.p[i][j]=min(C.p[i][j],A.p[i][k]+B.p[k][j]);
    return C;
}
inline void upd(int k,int now){
	memset(A[k].p,42,sizeof(A[k].p));
	for (rr int i=0;i<n;++i){
		if (!a[i][now]) continue;
		rr bool upw=0,dow=0;
		for (rr int j=0;j<n;++j){
			if (i+j>=n||!a[i+j][now]) upw=1;
			if (i<j||!a[i-j][now]) dow=1;
			if (upw&&dow) break;
			if (!upw) A[k].p[i+j][i]=j+1;
			if (!dow) A[k].p[i-j][i]=j+1;
		}
	}
}
inline void build(int k,int l,int r){
	if (l==r){upd(k,l); return;}
	rr int mid=(l+r)>>1;
	build(k<<1,l,mid),build(k<<1|1,mid+1,r);
	A[k]=mul(A[k<<1],A[k<<1|1]);
}
inline void query(int k,int l,int r,int x,int y){
	if (l==x&&r==y){
		if (!flag) ANS=A[k],flag=1;
		    else ANS=mul(ANS,A[k]);
		return;
	}
	rr int mid=(l+r)>>1;
	if (y<=mid) query(k<<1,l,mid,x,y);
	else if (x>mid) query(k<<1|1,mid+1,r,x,y);
	else query(k<<1,l,mid,x,mid),query(k<<1|1,mid+1,r,mid+1,y);
}
inline void update(int k,int l,int r,int x){
	if (l==r){upd(k,l); return;}
	rr int mid=(l+r)>>1;
	if (x<=mid) update(k<<1,l,mid,x);
        else update(k<<1|1,mid+1,r,x);
    A[k]=mul(A[k<<1],A[k<<1|1]);
}
signed main(){
	freopen("maze.in","r",stdin);
	freopen("maze.out","w",stdout);
	n=iut(); m=iut(); q=iut();
	for (rr int i=0;i<n;++i)
	for (rr int j=1;j<=m;++j) a[i][j]=iut();
	build(1,1,m);
	while (q--){
		rr int opt=iut(),l=iut()-1,r=iut();
		if (opt&1) a[l][r]^=1,update(1,1,m,r);
		else{
			rr int x=iut()-1,y=iut(),ans=2147483647; flag=0;
			if (y<r){printf("-1\n"); continue;}
			if (y==r){
				if (x>l) x^=l,l^=x,x^=l;
				for (rr int i=x;i<=l;++i)
				    if (!a[i][r]) flag=1;
				printf("%d\n",flag?-1:l-x);
			    continue;
			}
			query(1,1,m,r,y-1);
			rr bool upw=0,dow=0;
			for (rr int i=0;i<n;++i){
				if (x+i>=n||!a[x+i][y]) upw=1;
				if (x<i||!a[x-i][y]) dow=1;
				if (upw&&dow) break;
				if (!upw) ans=min(ans,ANS.p[l][x+i]+i);
				if (!dow) ans=min(ans,ANS.p[l][x-i]+i);
			}
			printf("%d\n",ans>n*m?-1:ans);
		}
	}
	return 0;
} 

JZOJ 6297 猛汉王

题目

在这里插入图片描述


分析

在这里插入图片描述


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
#define rr register
using namespace std;
struct rec{int bel,rk; long long x; int l,r;}p[300001];
struct lin{long long x,y;}a[100001],b[100001]; long long t[300001];
int n,m,d,c[300001],k,ansx[100001],ansy[100001]; long long ans1,ans2;
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;
}
bool cmp(rec a,rec b){return a.x!=b.x?a.x<b.x:b.bel!=0;}
inline void add(int x){for (;x<=k;x+=-x&x) ++c[x];}
inline signed query(int x){rr int ans=0; for (;x;x-=-x&x) ans+=c[x]; return ans;}
inline void pro(){
	memset(c,0,sizeof(c));
	k=0;
	for (rr int i=1;i<=m;++i) t[i]=b[i].y;
	for (rr int i=1;i<=n;++i) t[m+i]=a[i].y-d,t[n+m+i]=a[i].y+d;
	sort(t+1,t+1+n+n+m),k=unique(t+1,t+1+n+n+m)-t-1;
	for (rr int i=1;i<=m;++i)
		p[i]=(rec){0,0,b[i].x,lower_bound(t+1,t+1+k,b[i].y)-t,0};
    for (rr int i=1;i<=n;++i){
    	rr int t1=lower_bound(t+1,t+1+k,a[i].y-d)-t,t2=lower_bound(t+1,t+1+k,a[i].y+d)-t;
    	p[i+m]=(rec){-1,i,a[i].x-d-1,t1,t2},
    	p[i+m+n]=(rec){1,i,a[i].x+d,t1,t2};
	}
	sort(p+1,p+1+n+n+m,cmp);
	for (rr int i=1;i<=n+n+m;++i)
		if (!p[i].bel) add(p[i].l);
		else ansx[p[i].rk]+=p[i].bel*(query(p[i].r)-query(p[i].l-1));
}
signed main(){
	freopen("mhw.in","r",stdin);
	freopen("mhw.out","w",stdout); 
	n=iut(); m=iut(); d=iut();
	for (rr int i=1;i<=n;++i){
		rr int x=iut(),y=iut();
		a[i]=(lin){x+y,x-y};
	}
	for (rr int i=1;i<=m;++i){
		rr int x=iut(),y=iut();
		b[i]=(lin){x+y,x-y};
	}
	pro(),swap(a,b),swap(n,m),swap(ansx,ansy),pro(),
	sort(ansx+1,ansx+1+n),sort(ansy+1,ansy+1+m);
	for (rr int i=1;i<=n;++i)
		ans1+=1ll*ansx[i]*(n-i),ans1-=1ll*ansx[i]*(ansx[i]-1)>>1,
		ans2+=1ll*ansx[i]*(i-1),ans2-=1ll*ansx[i]*(ansx[i]-1)>>1;
	for (rr int i=1;i<=m;++i)
		ans1+=1ll*ansy[i]*(m-i),ans1-=1ll*ansy[i]*(ansy[i]-1)>>1,
		ans2+=1ll*ansy[i]*(i-1),ans2-=1ll*ansy[i]*(ansy[i]-1)>>1;
	return !printf("%lld %lld\n",ans1,ans2);
}

JZOJ 6299 工厂

题目

在这里插入图片描述


分析

在这里插入图片描述


代码

#include <cstdio>
#include <cstring>
#define rr register
#define sqr(x) ((x)*(x))
using namespace std;
struct rec{int x,y,w;}a[31];
bool mp[31][31],v1[31],v2[31];
int sum[31][31],n,tot,f[200001][31],c[31],g[31];
inline void dfs(int now,int opt){
	if (opt&1){
		++a[tot].x;
		for (rr int i=1;i<=n;++i)
		if (mp[now][i]&&!v2[i])
		    v2[i]=1,dfs(i,2);
	}else{
		++a[tot].y;
		for (rr int i=1;i<=n;++i)
		if (mp[i][now]&&!v1[i])
		    v1[i]=1,dfs(i,1);
	}
}
signed main(){
	freopen("factory.in","r",stdin);
	freopen("factory.out","w",stdout); 
	scanf("%d",&n); 
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<=n;++j){
		rr char c=getchar();
		while (c!=48&&c!=49) c=getchar();
		mp[i][j]=c^48;
	}
	for (rr int i=1;i<=n;++i)
	if (!v1[i]) ++tot,v1[i]=1,dfs(i,1);
	for (rr int i=1;i<=n;++i)
	if (!v2[i]) ++tot,v2[i]=1,dfs(i,2);//计算连通块
	for (rr int i=1;i<=tot;++i)
	    ++sum[a[i].x][a[i].y];//那么显然会有这么多种情况
	tot=0;
	for (rr int i=0;i<=n;++i)
	for (rr int j=0;j<=n;++j)
	if (sum[i][j]) a[++tot]=(rec){i,j,sum[i][j]};
	g[0]=1; memset(f,42,sizeof(f)),f[0][0]=0;
	for(rr int i=1;i<=tot;++i)
	    g[i]=g[i-1]*(a[i].w+1);//本质状态很少,可以这样优化转移状态
	for (rr int i=0;i<g[tot];++i){
		rr int t=i,tx=0,ty=0;
		for (rr int j=tot;j;--j){
			c[j]=0;
			while (t>=g[j-1])
			    ++c[j],t-=g[j-1],tx+=a[j].x,ty+=a[j].y;//计算其表示的状态
		}
		for (rr int j=0;j<=n;++j) if (f[i][j]!=707406378)
		for (rr int k=1;k<=tot;++k) if (c[k]<a[k].w){
			if (f[i+g[k-1]][j+a[k].x]>f[i][j]) f[i+g[k-1]][j+a[k].x]=f[i][j];
			if (tx+a[k].x==ty+a[k].y&&f[i+g[k-1]][0]>f[i][j]+sqr(j+a[k].x))
			    f[i+g[k-1]][0]=f[i][j]+sqr(j+a[k].x);//dp方程
		}
	}
	rr int ans=0;
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<=n;++j) ans-=mp[i][j];//也就是用dp的答案减去工人已经学会的技能的总和
	printf("%d",ans+f[g[tot]-1][0]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值