JZOJ 1402 偷懒的小X
题目
把一组数据按一定顺序依次插入数组中(即第 i i i个数为 a [ i ] a[i] a[i]),最后得出来就已经是一个堆,即不需要任何交换操作,若有多种方法,输出字典序最大的一组。满足 n = 2 x − 1 n=2^x-1 n=2x−1
分析
一开始脑残考虑每层从大到小倒序输出,结果小了,后来我突然想起了线段树,考虑像线段树那样反向分治,就A了
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
int n,a[65601],rk[65601];
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void dfs(int x,int l,int r){
rk[x]=l; if (l==r) return;
rr int mid=(l+r+1)>>1;
dfs(x<<1,mid+1,r);
dfs(x<<1|1,l+1,mid);
}
signed main(){
n=iut();
for (rr int i=1;i<=n;++i) a[i]=iut();
sort(a+1,a+1+n),dfs(1,1,n);
for (rr int i=1;i<=n;++i) print(a[rk[i]]),putchar(i==n?10:32);
return 0;
}
JZOJ 1403 渡河
代码(开O2AC的SPFA)
//SPFA理论O(n^4),实际开了O2快得飞起,正解缩点+bfs,但是懒得敲
#include <cstdio>
#include <cctype>
#include <queue>
#include <cstring>
#define rr register
using namespace std;
const int dx[8]={0,0,1,1,1,-1,-1,-1},dy[8]={1,-1,0,1,-1,0,1,-1};
struct rec{int x,y;}; int n,Q,dis[1011][1011];
bool a[1011][1011],v[1011][1011];
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>9) print(ans/10);
putchar(ans%10+48);
}
signed main(){
n=iut(),Q=iut();
memset(dis,42,sizeof(dis));
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<=n;++j){
rr char c=getchar();
while (!isdigit(c)) c=getchar();
a[i][j]=c^48;
}
rr queue<rec>q;
for (rr int i=0;i<n+2;++i) q.push((rec){i,0}),v[i][0]=1,dis[i][0]=0;
for (rr int i=0;i<n+2;++i) q.push((rec){i,n+1}),v[i][n+1]=1,dis[i][n+1]=0;
for (rr int i=1;i<=n;++i) q.push((rec){0,i}),v[0][i]=1,dis[0][i]=0;
for (rr int i=1;i<=n;++i) q.push((rec){n+1,i}),v[n+1][i]=1,dis[n+1][i]=0;
while (q.size()){
rr int qx=q.front().x,qy=q.front().y; q.pop();
for (rr int k=0;k<8;++k){
rr int nx=qx+dx[k],ny=qy+dy[k];
if (nx<1||ny<1||nx>n||ny>n||dis[nx][ny]<=dis[qx][qy]+(!a[qx][qy]&&a[nx][ny])) continue;
dis[nx][ny]=dis[qx][qy]+(!a[qx][qy]&&a[nx][ny]);
if (!v[nx][ny]) v[nx][ny]=1,q.push((rec){nx,ny});
}
v[qx][qy]=0;
}
for (rr int x,y;Q;--Q) x=iut(),y=iut(),print(dis[x][y]),putchar(32);
return 0;
}
JZOJ 1404 菱形内的计数
题目
一个边长为 n n n的大菱形被均匀地划分成了 n ∗ n n*n n∗n个边长为1的小菱形组成的网格,但是网格中部分边被抹去了,小L想知道,大菱形内有多少个平行四边形,这些平行四边形内不存在边。
分析
分类讨论,码量少,思维难度大
代码
#include <cstdio>
#include <cctype>
#include <queue>
#define rr register
using namespace std;
char str[1801][1801]; int n,s[1801][1801],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 signed exist(int dep,int l,int r){return s[dep][r-1]-s[dep][l];}
inline void dfs(int dep,int l,int r){
rr int lim=dep;
while (lim<2*n&&l>1&&r<2*n&&str[lim+1][l-1]==47&&str[lim+1][r+1]==92&&!exist(lim+1,l-1,r+1)) ++lim,--l,++r;
if (lim<2*n&&l>1&&str[lim+1][l-1]==47&&str[lim+1][r]==47&&!exist(lim+1,l-1,r)){
++lim,--l;
while (lim<2*n&&l>1&&str[lim+1][l-1]==47&&str[lim+1][r-1]==47&&!exist(lim+1,l-1,r-1)) ++lim,--l,--r;
--r;
}else if (lim<2*n&&r<2*n&&str[lim+1][l]==92&&str[lim+1][r+1]==92&&!exist(lim+1,l,r+1)){
++lim,++r;
while (lim<2*n&&r<2*n&&str[lim+1][l+1]==92&&str[lim+1][r+1]==92&&!exist(lim+1,l+1,r+1)) ++lim,++l,++r;
++l;
}
if (lim<2*n&&str[lim+1][l]==92&&str[lim+1][r]==47&&!exist(lim+1,l,r)){
++lim;
while (lim<2*n&&str[lim+1][l+1]==92&&str[lim+1][r-1]==47&&!exist(lim+1,l+1,r-1)) ++lim,++l,--r;
if (r-l==1) ++ans;
}
}
signed main(){
n=iut()<<1;
for (rr int i=1;i<=n;++i) gets(str[i]+1);
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<=n;++j) s[i][j]=s[i][j-1]+(str[i][j]==47||str[i][j]==92);
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<n;++j)
if (str[i][j]==47&&str[i][j+1]==92) dfs(i,j,j+1);
return !printf("%d",ans);
}
JZOJ 1405 电缆建设
题目
河流两旁各有 n , m n,m n,m个村庄,每个村庄可以用二维坐标表示,其中河流一旁的村庄横坐标均为 x 1 x1 x1,河流另一旁的村庄横坐标均为 x 2 x2 x2。由于地势十分开阔,任意两个村庄可以沿坐标系直线修建一条电缆连接,长度即为两村庄的距离。要修建若干条电缆,使得任意两个村庄都可以通过若干个有电缆连接的村庄相连。
分析
考虑河流一旁的村庄相邻连,对于每个村庄连接直线距离最短的两个村庄,用双指针实现,共有约 4 n 4n 4n条边,跑kruskal即可,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
代码
#include <cstdio>
#include <cctype>
#include <cmath>
#include <algorithm>
#define rr register
using namespace std;
const int N=600101;
struct node{int x,y; long long w;}e[N<<2];
int n,m,xx,a[N],b[N],f[N<<1],tot,now;
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 add(int x,int y,long long w){e[++tot]=(node){x,y,w};}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
inline long long w(int x){return 1ll*x*x;}
inline bool cmp(node a,node b){return a.w<b.w;}
signed main(){
n=iut(),m=iut(),xx=w(iut()-iut());
for (rr int i=1;i<=n;++i) a[i]=a[i-1]+iut(),f[i]=i;
for (rr int i=1;i<=m;++i) b[i]=b[i-1]+iut(),f[i+n]=i+n;
for (rr int i=1;i<n;++i) add(i,i+1,w(a[i+1]-a[i]));
for (rr int i=1;i<m;++i) add(i+n,i+1+n,w(b[i+1]-b[i]));
for (rr int i=1,j=1;i<=n;++i){
for (;j<=m&&b[j]<a[i];++j);
add(i,j+n,w(a[i]-b[j])+xx);
if (j>1) add(i,j+n-1,w(a[i]-b[j-1])+xx);
}
sort(e+1,e+1+tot,cmp); rr double ans=0;
for (rr int i=1;i<=tot;++i){
rr int fa=getf(e[i].x),fb=getf(e[i].y);
if (fa!=fb){
rr int fc=fa<fb?fa:fb;
f[fc]=fa^fb^fc,ans+=sqrt(e[i].w);
if (++now==n+m-1) return !printf("%.2lf",ans);
}
}
}