2019.11.09【NOIP提高組】模擬 B 組

JZOJ 1402 偷懶的小X

題目

把一組數據按一定順序依次插入數組中(即第ii個數爲a[i]a[i]),最後得出來就已經是一個堆,即不需要任何交換操作,若有多種方法,輸出字典序最大的一組。滿足n=2x1n=2^x-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 菱形內的計數

題目

一個邊長爲nn的大菱形被均勻地劃分成了nnn*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 電纜建設

題目

河流兩旁各有nmn,m個村莊,每個村莊可以用二維座標表示,其中河流一旁的村莊橫座標均爲x1x1,河流另一旁的村莊橫座標均爲x2x2。由於地勢十分開闊,任意兩個村莊可以沿座標系直線修建一條電纜連接,長度即爲兩村莊的距離。要修建若干條電纜,使得任意兩個村莊都可以通過若干個有電纜連接的村莊相連。


分析

考慮河流一旁的村莊相鄰連,對於每個村莊連接直線距離最短的兩個村莊,用雙指針實現,共有約4n4n條邊,跑kruskal即可,時間複雜度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);
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章