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;
}