NOIP模擬76

Varuxn發表於2021-10-15

前言

還有不到 10 天就要 CSP-S 。。。馬上我就要有我的第一篇遊記了。

今天考試莽了一回,整了大概 2.5h 的 T1 ,可能是因為今天題目比較難,看起來成效不錯。

以後還是要注意時間的分配(可是今天后面的題目看起來是真不可做。。)

T1 洛希極限

解題思路

正解不會。。但是貌似 \(\mathcal{O}(nm+nmlogn)\) 吸氧可以過。。(達成成就:考場唯一切題,切了但沒完全切)

發現如果沒有矩形的限制的話對於一個格子 \((i,j)\) 轉移狀態可以來自以它為右下角的矩形區域。

那麼考慮優化,對於一個在上述合法區域內的點,如果它與 \((i,j)\) 的橫座標和者縱座標之差都超過了 1 ,那麼這個節點一定不是最大值。

至於原因的話,它一定可以跳到某一箇中轉點,於是我們成功的將複雜度由 \((n^4)\) 降到了 \(n^3\)

然後再每一行每一列開一顆線段樹維護就可以得到 \(n^2logn\) 的複雜度。

然後瓶頸在於初始化每個點列和行上可以達到的最小值,然後我考場上就開始瞎搞,複雜度就玄學。。。(code

一開始看了看大樣例吸氧 3.7s 左右,這不是穩過??,然後一看,評測機居然沒開氧氣,我***。

嘗試著不吸氧 12s 於是開始大力剪枝+卡常,最終在 OJ 上只多了 5pts 這麼點???,我的四十分鐘啊(卡常之後,OJ沒過但是教師機過了的code

考後問了一下 zero4338 大概就是維護一個連結串列(戰神叫他並茶几。。)每次更新列的。

如果一行都跳完了行指標下移就好了。

然後我發現對於列的限制用連結串列較快,對於行的限制我的瞎搞做法較快。

如果 OJ 沒有氧的話就需要繼續卡常+非法預處理了(逃

code

//%:pragma GCC optimize(2)
//%:pragma GCC optimize(3)
//%:pragma GCC optimize("inline")
//%:pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=2e3+10,M=5e5+10,INF=1e9;
const int mod=1e9+7;
int T,q,n,m,cnt,nxt[N][N],nxt2[N],h[N][N],z[N][N];
struct Node{int x,y,x2,y2;}s[M];
struct node
{
	int mx,dat;
	inline node friend operator + (node x,node y)
	{
		if(x.mx>y.mx) return x;
		if(y.mx>x.mx) return y;
		register int temp=1ll*x.dat+y.dat;
		if(temp>=mod) temp-=mod;
		return (node){x.mx,temp};
	}
}ans;
#define int register int
struct Segment_Tree
{
	node tre[N<<2];
	#define push_up(x) tre[x]=tre[ls]+tre[rs];
	inline void build(int x,int l,int r)
	{
		if(l==r) return tre[x].mx=tre[x].dat=1,void();
		int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r);
		push_up(x);
	}
	inline void insert(int x,int l,int r,int pos,node val)
	{
		if(l==r) return tre[x]=tre[x]+val,void();
		int mid=(l+r)>>1;
		if(pos<=mid) insert(ls,l,mid,pos,val);
		else insert(rs,mid+1,r,pos,val);
		push_up(x);
	}
	inline node query(int x,int l,int r,int L,int R)
	{
		if(L<=l&&r<=R) return tre[x];
		int mid=(l+r)>>1;
		if(L<=mid&&R>mid) return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
		if(L<=mid) return query(ls,l,mid,L,R); return query(rs,mid+1,r,L,R);
	}
}H[N],Z[N];
bool comp(Node a,Node b)
{
	if(a.x!=b.x) return a.x<b.x;
	if(a.y!=b.y) return a.y<b.y;
	if(a.x2!=b.x2) return a.x2<b.x2;
	return a.y2<b.y2;
}
bool check(Node a,Node b){return a.x<=b.x&&a.x2>=b.x2&&a.y<=b.y&&a.y2>=b.y2;}
void solve()
{
	n=read(); m=read(); q=read(); cnt=1; ans=(node){0,0};
	for(int i=1,x,y,x2,y2;i<=q;i++)
		x=read(),y=read(),x2=read(),y2=read(),
		s[i]=(Node){x,y,x2,y2};
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) h[i][j]=z[i][j]=INF;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) nxt[i][j]=j+1;
	for(int i=1;i<=n+1;i++) nxt2[i]=i+1;
	sort(s+1,s+q+1,comp); reverse(s+1,s+q+1);
	for(int i=2;i<=q;i++) if(!check(s[i-1],s[i])) s[++cnt]=s[i];
	reverse(s+1,s+cnt+1);
	for(int i=1;i<=cnt;i++)
	{
		int x=s[i].x,y=s[i].y,x2=s[i].x2,y2=s[i].y2;
		for(int j=x+1;j<=x2;j=nxt2[j])
		{
			for(int k=y+1;k<=y2;k=nxt[j][k]) z[j][k]=min(z[j][k],x);
			for(int k=y+1,pre=nxt[j][k];k<=y2;k=pre,pre=nxt[j][k])
				nxt[j][k]=max(nxt[j][k],y2+1);
			if(nxt[j][1]>m) nxt2[j]=nxt2[j+1];
		}
	}
	for(int i=1;i<=cnt;i++)
	{
		int x=s[i].x,y=s[i].y,x2=s[i].x2,y2=s[i].y2;
		for(int j=x2;j>=x+1;j--)
			for(int k=y2;k>=y+1;k--)
			{
				if(h[j][k]<=s[i].y) break;
				h[j][k]=s[i].y;
			}
		
	}
	for(int i=1;i<=n;i++) H[i].build(1,1,m);
	for(int i=1;i<=m;i++) Z[i].build(1,1,n);
	for(int i=2;i<=n;i++)
		for(int j=2;j<=m;j++)
		{
			if(h[i][j]==INF||z[i][j]==INF) continue;
			node temp=H[i-1].query(1,1,m,h[i][j],j-1);
			if(i-2>=z[i][j]) temp=temp+Z[j-1].query(1,1,n,z[i][j],i-2);
			temp.mx++;
			H[i].insert(1,1,m,j,temp); Z[j].insert(1,1,n,i,temp);
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			ans=ans+H[i].query(1,1,m,j,j);
	printf("%d %d\n",ans.mx,ans.dat);
}
signed main()
{
	freopen("roche.in","r",stdin); freopen("roche.out","w",stdout);
	T=read(); while(T--) solve();
	return 0;
}

T2 特立獨行的圖

大坑未補

T3 玩遊戲

期望+調和級數+求導(我哪會啊!!)抄題解都懶得抄。。

大坑未補

T4 駱駝

解題思路

對於 5 的情況直接處理即可。

然後考慮將所有的比較大塊的矩形分成比較小的幾塊 \(5\times 5\) 拼起來。

對於多種情況轉移就好了,需要對於奇數偶數塊數分別判斷。。

code

#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
#define left Left
#define right Right
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e3+10,M=210;
const int up=4,down=3,left=5,right=6;
int n,m,suc,s[N][N],ans[N][N];
int d1[10]={0,-3,3,0,0,-2,-2,2,2};
int d2[10]={0,0,0,3,-3,2,-2,2,-2};
int c[10][5][5]=
{
	{{1,8,16,2,7},{11,19,5,10,18},{22,14,0,21,15},{4,9,17,3,6},{12,20,23,13,0}},
	{{22,3,9,23,2},{16,25,20,17,7},{10,13,1,4,12},{21,18,8,24,19},{15,5,11,14,6}},
	{{1,12,5,2,13},{7,18,15,10,19},{23,3,0,22,4},{16,11,6,17,14},{8,21,24,9,20}},
	{{6,22,14,7,2},{19,9,4,20,12},{24,16,1,23,15},{5,21,13,8,3},{18,10,25,17,11}},
	{{9,22,25,8,2},{17,12,4,20,15},{24,7,1,23,6},{10,21,16,11,3},{18,13,5,19,14}},
	{{9,17,24,8,2},{22,12,4,19,13},{25,7,1,16,6},{10,18,23,11,3},{21,15,5,20,14}},
	{{22,14,7,23,2},{9,19,4,12,18},{6,24,1,15,25},{21,13,8,20,3},{10,16,5,11,17}}
};
struct Node{int base,id;}p[M][M];
void dfs(int x,int y,int cnt)
{
	if(suc) return ;
	s[x][y]=cnt;
	if(cnt==n*n)
	{
		int can=false;
		for(int i=1;i<=8;i++)
		{
			int x2=x+d1[i],y2=y+d2[i];
			if(x2>n||x2<1||y2<1||y2>n) continue;
			if(x2==1&&y2==1){can=true;break;}
		} suc|=can;
		if(can)
			for(int i=1;i<=n;i++)
				for(int j=1;j<=n;j++)
					ans[i][j]=s[i][j];
		return s[x][y]=0,void();
	}
	for(int i=1;i<=8;i++)
	{
		int x2=x+d1[i],y2=y+d2[i];
		if(x2>n||x2<1||y2<1||y2>n) continue;
		if(s[x2][y2]) continue;
		dfs(x2,y2,cnt+1);
	}
	s[x][y]=0;
}
void work(int x,int y,Node temp)
{
	for(int i=1;i<=5;i++)
		for(int j=1;j<=5;j++)
			ans[i+x][j+y]=c[temp.id][i-1][j-1]+temp.base;
}
void Solve_For_Odd()
{
	p[1][1].id=0; p[2][2].id=1; p[m][1].id=right; p[1][2].id=down;
	for(int i=3;i<=m;i++)
		if(i&1)
		{
			p[2][i].id=p[i][m].id=up; p[1][i].id=left;
			for(int j=2;j<m;j++) p[i][j].id=right;
		}
		else
		{
			p[2][i].id=left; p[i][2].id=up; p[1][i].id=down;
			for(int j=3;j<=m;j++) p[i][j].id=left;
		}
	for(int i=2;i<=m-1;i++) p[i][1].id=down;
}
void Solve_For_Even()
{
	p[1][1].id=2; p[m][1].id=right;
	for(int i=2;i<=m;i++)
	{
		if(i!=m) p[i][1].id=down; p[1][i].id=left;
		if(i&1)
		{
			for(int j=3;j<=m;j++) p[i][j].id=left;
			p[i][2].id=up; continue;
		}
		for(int j=2;j<m;j++) p[i][j].id=right;
		p[i][m].id=up;
	}
}
void solve(int x,int y,int base)
{
	while(p[x][y].id&&p[x][y].id!=1&&p[x][y].id!=2)
	{
		p[x][y].base=base; base+=25;
		if(p[x][y].id==down) x++;
		else if(p[x][y].id==up) x--;
		else if(p[x][y].id==right) y++;
		else if(p[x][y].id==left) y--;
	}
	if(p[x][y].id==1) p[x][y].base=base;
	for(int i=1;i<=m;i++)
		for(int j=1;j<=m;j++)
			work((i-1)*5,(j-1)*5,p[i][j]);
}
void print(){for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%lld ",ans[i][j]);printf("\n");}}
signed main()
{
	freopen("camel.in","r",stdin); freopen("camel.out","w",stdout);
	n=read(); m=n/5; if(n==5) dfs(1,1,1),print(),exit(0);
	if(m&1) Solve_For_Odd(),solve(2,1,23),ans[5][5]=n*n-1;
	else Solve_For_Even(),solve(2,1,24);
	ans[3][3]=n*n; print();
	return 0;
}