noip模拟76(差分约束待补,导数待补)

A. 洛希极限

考场上想的还是太少了.

发现每个点被转移,最优肯定是从自己斜下方转移.

很直接的想法就是线段树,发现每个点只会取最优的,于是可以做一个单调队列.

A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
	#define ll int
	#define ull unsigned ll
	#define lf long double
	#define lbt(x) (x&(-x))
	#define mp(x,y) make_pair(x,y)
	#define lb lower_bound 
	#define ub upper_bound
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memcpy(x,y,sizeof x)
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	inline ll read() {
		ll w=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0; 
		while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
		return cit?w:-w;
	}
} using namespace BSS;

const ll N=3e3+21,Q=5e5+21,mod=1e9+7;

ll m,n,ops;
ll bin[Q];
ll le[N][N],ri[N][N],dn[N][N],up[N][N];
struct I { ll r1,c1,r2,c2; } ma[Q],mat[Q];
struct II { 
	ll len,w; 
	friend bool operator <(II i,II j){
		return i.len<j.len;	
	}
	friend II operator +(II i,II j){
		if(j.len>i.len) return j;
		return i.w=(i.w+(i.len==j.len)*j.w)%mod,i;
	}
} f[N][N];
auto comp=[](I i,I j)->bool{
	if(i.r1!=j.r1) return i.r1<j.r1;
	if(i.r2!=j.r2) return i.r2<j.r2;
	if(i.c1!=j.c1) return i.c1<j.c1;
	return i.c2<j.c2;
};
ll getri(ll x,ll y){ return ri[x][y]==0 ? x : ri[x][y]=getri(ri[x][y],y); }
ll getup(ll x,ll y){ return up[x][y]==0 ? y : up[x][y]=getup(x,up[x][y]); }
struct Dull_queue{
	ll head,tail; ll cnt[N],pos[N]; II que[N];
	inline void clr(){
		head=1,tail=0; ll lmt=min(n,m);
		for(ll i=0;i<=lmt;i++) cnt[i]=0,pos[i]=0;
	}
	inline void push(ll x,II y){
		while(head<=tail and que[tail]<y)
			cnt[que[tail].len]=(cnt[que[tail].len]-que[tail].w+mod)%mod,tail--;
		que[++tail]=y,pos[tail]=x,cnt[y.len]=(cnt[y.len]+y.w)%mod;
	}
	inline II query(ll x){
		while(head<=tail and pos[head]<x) 
			cnt[que[head].len]=(cnt[que[head].len]-que[head].w+mod)%mod,head++;
		// cout<<que[head].len<<' '<<cnt[que[head].len]<<" piu\n";
		return (II){que[head].len+1,cnt[que[head].len]};
	}
}qs[N],qh[N];
auto Work=[]()->void{
	n=read(),m=read(),ops=read();
	for(ll i=0;i<=n;i++) bin[i]=0;
	for(ll i=1;i<=ops;i++){
		ma[i].r1=read(),ma[i].c1=read(),ma[i].r2=read(),ma[i].c2=read();
		bin[ma[i].r1]++; 
	}
	for(ll i=1;i<=n;i++) bin[i]+=bin[i-1];
	for(ll i=1;i<=ops;i++) mat[bin[ma[i].r1]--]=ma[i];
	for(ll i=1;i<=ops;i++){
		for(ll y=mat[i].c1+1;y<=mat[i].c2;y++)
			for(ll x=mat[i].r1+1;x<=mat[i].r2;)
				ri[x][y] ? x=getri(x,y) : (le[x][y]=mat[i].r1,ri[x][y]=x+1,x++) ;
	}
	for(ll i=0;i<=m;i++) bin[i]=0;
	for(ll i=1;i<=ops;i++) bin[ma[i].c1]++;
	for(ll i=1;i<=m;i++) bin[i]+=bin[i-1];
	for(ll i=1;i<=ops;i++) mat[bin[ma[i].c1]--]=ma[i];
	for(ll i=1;i<=ops;i++){
		for(ll x=mat[i].r1+1;x<=mat[i].r2;x++)
			for(ll y=mat[i].c1+1;y<=mat[i].c2;)
				up[x][y] ? y=getup(x,y) : (dn[x][y]=mat[i].c1,up[x][y]=y+1,y++) ;
	}
	for(ll i=1;i<=n;i++){
		for(ll j=1;j<=m;j++) f[i][j]=(II){1,1};
	}
	for(ll i=1;i<=n;i++) qs[i].clr();
	for(ll i=1;i<=m;i++) qh[i].clr();
	for(ll i=1;i<=n;i++){
		for(ll j=1;j<=m;j++){
			qs[i].push(j,f[i][j]),qh[j].push(i,f[i][j]);
			if(i<n and j<m and dn[i+1][j+1]){
				// cout<<f[i][j].len<<' '<<f[i][j].w<<' '<<f[i+1][j+1].len<<' '<<f[i+1][j+1].w<<endl;
				f[i+1][j+1]=f[i+1][j+1]+qs[i].query(dn[i+1][j+1]);
				// cout<<f[i][j].len<<' '<<f[i][j].w<<' '<<f[i+1][j+1].len<<' '<<f[i+1][j+1].w<<" skr\n";
				f[i+1][j+1]=f[i+1][j+1]+qh[j].query(le[i+1][j+1]);
				// cout<<f[i][j].len<<' '<<f[i][j].w<<' '<<f[i+1][j+1].len<<' '<<f[i+1][j+1].w<<" der\n";
				f[i+1][j+1].w=(f[i+1][j+1].w-f[i][j].w*(f[i][j].len+1==f[i+1][j+1].len)+mod)%mod;
			}
		}
	}
	// for(signed long long int i=1;i<=n;i++){
	// 	for(int j=1;j<=m;j++){
	// 		cout<<f[i][j].len<<' '<<f[i][j].w<<endl;
	// 	}
	// 	puts("");
	// }
	II ans=(II){0,0};
	for(ll i=1;i<=n;i++){
		for(ll j=1;j<=m;j++){
			ans=ans+f[i][j];
			le[i][j]=0,ri[i][j]=0,up[i][j]=0,dn[i][j]=0;
		}
	}
	printf("%d %d\n",ans.len,ans.w);
};
signed main(){
	File(roche);
	for(int Ts=read();Ts;Ts--) Work();
	exit(0);
}

B. 特立独行的图

构造题,目前只写了判是否有解,差分约束还没打完.

C. 玩游戏

求导不会,鸽了.

D. 骆驼

打表发现,在任意一个 \(5∗5\) 的棋盘内,从任意一个点出发,都可以到达把棋盘走完后到达棋盘上的另外一个点.

关于更有说服力的证明,可以选择把可以互相到达的点之间连一条边,发现可以构成欧拉路.

发现数据范围,\(N\) 都是 \(5\) 的倍数,于是一个很自然的想法就应该是选择把大棋盘拆成小棋盘.

于是构造就很容易了,灵活使用 \((3,3)\) 的位置,代码会比较好写.

D_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
	#define ll int
	#define ull unsigned ll
	#define lf long double
	#define lbt(x) (x&(-x))
	#define mp(x,y) make_pair(x,y)
	#define lb lower_bound 
	#define ub upper_bound
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memcpy(x,y,sizeof x)
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	inline ll read() {
		ll w=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0; 
		while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
		return cit?w:-w;
	}
} using namespace BSS;

const ll val[8][6][6]={
	{ {0,0,0,0,0,0},{0,0,0,0,0,0},{0,0,0,0,0,0},{0,0,0,0,0,0},{0,0,0,0,0,0},{0,0,0,0,0,0} },
	{ {0,0,0,0,0,0},{0,1,19,7,4,20},{0,14,11,22,17,12},{0,8,5,0,9,6},{0,2,18,13,3,21},{0,15,10,23,16,0} },
	{ {0,0,0,0,0,0},{0,2,8,24,17,9},{0,13,19,4,12,22},{0,6,16,1,7,25},{0,3,11,23,18,10},{0,14,20,5,15,21} },
	{ {0,0,0,0,0,0},{0,2,15,24,8,16},{0,22,10,4,19,11},{0,25,7,1,14,6},{0,3,18,23,9,17},{0,21,13,5,20,12} },
	{ {0,0,0,0,0,0},{0,2,7,14,22,6},{0,12,20,4,9,19},{0,15,23,1,16,24},{0,3,8,13,21,5},{0,11,17,25,10,18} },
	{ {0,0,0,0,0,0},{0,2,8,25,22,9},{0,15,20,4,12,17},{0,6,23,1,7,24},{0,3,11,16,21,10},{0,14,19,5,13,18} },
	{ {0,0,0,0,0,0},{0,2,15,8,5,16},{0,10,25,18,13,24},{0,20,6,1,21,7},{0,3,14,9,4,17},{0,11,22,19,12,23} },
	{ {0,0,0,0,0,0},{0,1,6,13,21,5},{0,11,19,3,8,18},{0,14,22,0,15,23},{0,2,7,12,20,4},{0,10,16,24,9,17} } 
};
const ll N=1e3+21;

ll m,n,sum;
ll ans[N][N];
auto update=[](ll x,ll y,ll id)->void{
	ll valx=5*(x-1),valy=5*(y-1);
	for(ll i=1;i<=5;i++){
		for(ll j=1;j<=5;j++)
			ans[valx+i][valy+j]=val[id][i][j]+sum;
	}
};
auto Work1=[]()->void{
	update(1,1,1),sum+=23;
	for(ll i=2;i<m;i++) update(i,1,4),sum+=25;
	update(m,1,2),sum+=25;
	for(ll i=m;i>=3;i--){
		if(i&1){
			for(ll j=2;j<m;j++) update(i,j,2),sum+=25;
			update(i,m,5),sum+=25;
		}
		else{
			for(int j=m;j>=3;j--) update(i,j,3),sum+=25;
			(i^1) ? (update(i,2,5),sum+=25) : (update(i,2,3),sum+=25) ;
		}
	}
	for(ll i=m;i>=3;i--){
		if(i&1) update(2,i,5),sum+=25,update(1,i,3),sum+=25;
		else update(1,i,4),sum+=25,update(2,i,3),sum+=25;
	}
	update(1,2,4),sum+=25,update(2,2,6),ans[5][5]=n*n-1,ans[3][3]=n*n;
};
auto Work2=[]()->void{
	update(1,1,7),sum+=24;
	for(ll i=2;i<m;i++) update(i,1,4),sum+=25;
	update(m,1,2),sum+=25;
	for(ll i=m;i>=1;i--){
		if(i&1){
			for(ll j=m;j>=3;j--) update(i,j,3),sum+=25;
			(i^1) ? (update(i,2,5),sum+=25) : (update(i,2,3),sum+=25) ;
			
		}
		else{
			for(ll j=2;j<m;j++) update(i,j,2),sum+=25;
			update(i,m,5),sum+=25;
		}
	}
	ans[3][3]=n*n;
};
signed main(){
	File(camel);
	n=read(),m=n/5;
	if(n==5) puts("1 9 17 2 10"),puts("15 23 6 14 22"),puts("18 3 11 19 4"),puts("25 8 16 24 7"),puts("12 20 5 13 21"),exit(0);
	(n&1) ? Work1() : Work2() ;
	for(ll i=1;i<=n;i++){
		for(ll j=1;j<=n;j++)
			printf("%d ",ans[i][j]);
		puts("");
	}
}
上一篇:noip模拟78


下一篇:NOIP模拟78