感受到了绝望
JZOJ NO.1 教主的花园
分析
当没有墙的直接求曼哈顿距离,否则二分求最近的出口。
三种情况:
(1)
没有墙
(2)
出口在两点之间
(3)
出口在两点之外
代码
#include <cstdio>
#include <algorithm>
#include <cctype>
using namespace std;
int a[100001],n,m;
int abs(int x){if (x<0) return -x; else return x;}
int erfen(int nx,int qx){
int l=1,r=n,mid=(1+n)>>1,ans=2147483647;
while (l<=r){
if (nx>a[mid]&&qx<a[mid]||nx<a[mid]&&qx>a[mid]) return 0;//最近的出口在两点之间
if (min(abs(a[mid]-nx),abs(a[mid]-qx))*2<ans)
ans=min(abs(a[mid]-nx),abs(a[mid]-qx))*2;//最近的出口在两点之外
if (a[mid]<nx) l=mid+1; else r=mid-1;//二分查找
mid=(l+r)>>1;
}
return ans;
}
int in(){
char c=getchar(); int ans=0,f=1;
while (!isdigit(c)&&c!='-') c=getchar();
if (c=='-') f=-f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
int main(){
n=in(); for (int i=1;i<=n;i++) a[i]=in();
stable_sort(a+1,a+1+n); m=in();
for (int i=1;i<=m;i++){
int nx,ny,qx,qy,ans; nx=in(); ny=in(); qx=in(); qy=in();
ans=abs(nx-qx)+abs(ny-qy); //曼哈顿距离
if (ny<0&&qy>0||ny>0&&qy<0)//需要寻找最近的出口
printf("%d\n",erfen(nx,qx)+ans); else printf("%d\n",ans);
} return 0;
}
JZOJ NO.2 教主泡嫦娥
分析
dp,注意它是一圈。把环展开。
状态转移方程:f[i][j][k]f[i][j][k]f[i][j][k]表示走到第iii步,状态为jjj,在当前有没有改变状态。
f[i][j][0]=f[i-1][j][0]+|a[i]-a[i-1]| 直接走过
f[i][j][1]=min(f[i-1][j][1],min(f[i-1][j^1][0],f[i-1][j^1][1])+m)+|a[i]-a[i-1]| 改变状态
f[i][j][0]=f[i-1][j][0]+(a[i]-a[i-1])^2 直接走过
f[i][j][1]=min(f[i-1][j][1],min(f[i-1][j^1][0],f[i-1][j^1][1])+m)+(a[i]-a[i-1])^2 改变状态
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define ull unsigned long long
using namespace std;
int n,a[10001],m; ull f[2][2][2],ans;
int in(){
char c=getchar(); int ans=0;
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void mem(){f[0][0][0]=f[0][0][1]=f[0][1][1]=f[0][1][0]=f[1][0][0]=f[1][0][1]=f[1][1][1]=f[1][1][0]=0;}
ull abs(ull x){return (x>0)?x:-x;} ull sqr(ull x){return x*x;}
void dp(){
f[0][1][1]=f[0][0][1]=1e17-1;
for (int i=1;i<=n;i++)
for (int j=0;j<=1;j++)
if ((a[i]<a[i-1])^j){//状态相同
f[i&1][j][0]=f[i&1^1][j][0]+abs(a[i]-a[i-1]);
f[i&1][j][1]=min(f[i&1^1][j][1],min(f[i&1^1][j^1][0],f[i&1^1][j^1][1])+m)+abs(a[i]-a[i-1]);
}else{
f[i&1][j][0]=f[i&1^1][j][0]+sqr(a[i]-a[i-1]);
f[i&1][j][1]=min(f[i&1^1][j][1],min(f[i&1^1][j^1][0],f[i&1^1][j^1][1])+m)+sqr(a[i]-a[i-1]);
}
}
int main(){
n=in(); m=in();
for (int i=0;i<n;i++) a[i]=in(); a[n]=a[0];//展开成环
mem(); dp(); ans=min(min(f[n&1][0][0],f[n&1][0][1]),min(f[n&1][1][1],f[n&1][1][0]));//4种情况的最小值
mem(); f[0][0][0]=1e17-1; dp(); ans=min(ans,f[n&1][1][1]-m);//一开始下降
mem(); f[0][1][0]=1e17-1; dp(); ans=min(ans,f[n&1][0][1]-m);//一开始上升
printf("%llu",ans); return 0;
}
JZOJ NO.3 保镖排队
分析
树形dp
状态转移方程:f[x]=(f[x]∗f[e[t].y]∗co[sum[x]−1][sum[e[t].y]−1])f[x]=(f[x]*f[e[t].y]*co[sum[x]-1][sum[e[t].y]-1])f[x]=(f[x]∗f[e[t].y]∗co[sum[x]−1][sum[e[t].y]−1])
co表示组合公式c(n,m)插空法
代码
#include <cstdio>
#include <cstring>
using namespace std;
struct tree{
int y,next;
}e[2001];
int T,n,f[1001],k,m,sum[1001],co[1001][1001],ls[1001];
void dp(int x){
int t=ls[x]; f[x]=1;
if (!t) sum[x]=1;//没有孩子
else{
while (t){
dp(e[t].y);
sum[x]+=sum[e[t].y];//孩子有多少个孩子也就是父亲还有多少个孩子
f[x]=(f[x]*f[e[t].y]%10007*co[sum[x]-1][sum[e[t].y]-1]%10007);//状态转移(不包括自己)
t=e[t].next;
}
sum[x]++;//算上自己
}
}
int main(){
co[0][0]=1;for (int i=1;i<=1000;i++){co[i][0]=1;
for (int j=1;j<=i;j++) co[i][j]=(co[i-1][j]+co[i-1][j-1])%10007;}//杨辉三角
scanf("%d",&T);
while (T--){
memset(ls,0,sizeof(ls));memset(sum,0,sizeof(sum));scanf("%d",&n);for(int i=1;i<=n;i++)
{scanf("%d",&k);for (int j=1;j<=k;j++) scanf("%d",&e[++m].y),e[m].next=ls[i],ls[i]=m; }//邻接表
dp(1); printf("%d\n",f[1]);}//保镖1没有根
return 0;
}
JZOJ NO.4 教主的别墅
分析
贪心,用前缀和求出1~i房间的男女差值。最大差值为sum/msum/msum/m,正推+反推
代码
#include <cstdio>
#include <cmath>
using namespace std;
int n,m,ans,a[100001],sum[5000001]; char c;
int abs(int x){if (x<0) return -x; else return x;}
int main(){
scanf("%d%d\n",&n,&m);
for (int i=1;i<=n;i++){
c=getchar();
if (c=='0') sum[i]--; else sum[i]++;
sum[i]+=sum[i-1];//前缀和
}
ans=ceil((double)abs(sum[n])/m);//最大差值(向上取整)
if (!sum[n]){
int s=0;
for (int i=1;i<=n;i++)
if (sum[i]==0) s++;
if (s>=m) ans=0; else ans=1;}//没有差值吗?
int t=m,x=0;
for (int i=1;i<=n;i++){
int answ=ceil((double)abs(sum[n]-sum[i])/(t-1));
if (abs(sum[i]-sum[x])<=ans&&answ<=ans) printf("%d ",i-x),x=i,t--;//可以分
if (t==1) break;
} printf("%d",n-x);
t=m,x=n; int d=0;
for (int i=n-1;i>=1;i--){
int answ=ceil((double)abs(sum[i])/(t-1));
if (abs(sum[i]-sum[x])<=ans&&answ<=ans) a[++d]=x-i,x=i,t--;//可以分
if (t==1) break;
} a[++d]=x;
for (int i=d;i>=1;i--) printf("%c%d",(i==d)?'\n':' ',a[i]);
return 0;
}