JeremyGuo的NOIP(+)模拟赛

21 篇文章 0 订阅
10 篇文章 0 订阅
T1 水池

在这里插入图片描述
n , m , L ≤ 1000 n,m,L\le1000 n,m,L1000

题目分析

考虑积水从最低的高度慢慢往上涨,什么时候会流出去,记vis[i][j]=1表示(i,j)已经流出去了。
从小到大枚举高度H,枚举高度=H的点(用vector实现),在点四周的高度<=H的块相当于连通了。如果四周有一个已经流出去的块,意味着这整个连通块(除了已经流出去的部分)在积水高度超过H的时候就会流出去,bfs整个连通块,没有vis过的点的答案高度就是H-h[i][j],vis过的点不再访问;如果四周没有流出去的块,意味着整个连通块在H的高度还能继续积水。
总复杂度 O ( n 2 ) O(n^2) O(n2)

纪念我第一次被卡常:

  • dfs改成bfs,2000ms → \rarr 1300ms
  • vis[1000000]改成vis[1000][1000],1300ms → \rarr 700ms
  • 输入输出优化,700ms → \rarr 200ms

QWQ。。。

Code:

#include<bits/stdc++.h>
#define maxn 1005
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
typedef pair<int,int> pii;
int n,m,L,h[maxn][maxn],ans[maxn][maxn];
vector<pii>pos[maxn];
bool vis[maxn][maxn];
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
queue<pii>q;
void bfs(int H){
	int u,v,x,y; pii S;
	while(!q.empty()){
		S=q.front();q.pop();
		ans[u=S.first][v=S.second]=H;
		for(int k=0;k<4;k++) if(h[x=u+dx[k]][y=v+dy[k]]<=H&&!vis[x][y]) 
			vis[x][y]=1,q.push(pii(x,y));
	}
}
void print(int x){
	if(x>9) print(x/10);
	putchar(x%10+48);
}
int main()
{
	freopen("pool.in","r",stdin);
	freopen("pool.out","w",stdout);
	read(n),read(m),read(L);
	for(int i=1;i<=n;i++) 
		for(int j=1;j<=m;j++) 
			read(h[i][j]),pos[h[i][j]].push_back(pii(i,j));
	for(int i=0;i<=n+1;i++) vis[i][0]=vis[i][m+1]=1;
	for(int j=0;j<=m+1;j++) vis[0][j]=vis[n+1][j]=1;
	for(int i=0;i<=L;i++){
		for(int j=pos[i].size()-1;j>=0;j--){
			int u=pos[i][j].first,v=pos[i][j].second,x,y,flg=0;
			for(int k=0;k<4;k++) if(h[x=u+dx[k]][y=v+dy[k]]<=i) flg|=vis[x][y];
			if(flg) q.push(pos[i][j]),vis[u][v]=1;
		}
		if(!q.empty()) bfs(i);
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			print(ans[i][j]-h[i][j]),putchar(j==m?10:32);
}

T2 排列

在这里插入图片描述
T ≤ 10 , n ≤ 200 T\le10,n\le200 T10,n200

题目分析

这道题要是想什么按顺序从大到小放区间你就自闭了。。。
你会发现一个值覆盖的区间不一定要包含自己。。
发现两个答案序列不同当前仅当有一位不同,我们不去想操作是怎么一步一步做的,只需要知道答案序列长什么样子就好了,我们可以从答案序列推出用了多少次操作。

分析一下答案序列长什么样,一个值最后肯定一段连续的区间,不会分开(相当于自己覆盖一整段再被左右两边盖去一部分)。
顺着放序列的每一位,设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示放到了答案序列的第 i i i位,放完了原序列的前 j j j个数,用了 k k k次操作的方案数。
用第 j j j个数在答案序列中的区间范围进行转移:

  • j j j个数不放, f [ i ] [ j ] [ k ] = f [ i ] [ j − 1 ] [ k ] f[i][j][k]=f[i][j-1][k] f[i][j][k]=f[i][j1][k]
  • i ∈ [ L [ j ] , R [ j ] ] i\in[L[j],R[j]] i[L[j],R[j]],在这个区间内第 j j j个数是最大值。
    • i = = j i==j i==j,第 j j j个数放在第 i i i位, f [ i ] [ j ] [ k ] + = f [ i − 1 ] [ j − 1 ] [ k ] f[i][j][k]+=f[i-1][j-1][k] f[i][j][k]+=f[i1][j1][k]
    • i ! = j i!=j i!=j,第 j j j个数放在第 i i i位, f [ i ] [ j ] [ k ] + = f [ i − 1 ] [ j − 1 ] [ k − 1 ] f[i][j][k]+=f[i-1][j-1][k-1] f[i][j][k]+=f[i1][j1][k1]
    • j j j个数放在区间 [ t + 1 , i ] [t+1,i] [t+1,i] f [ i ] [ j ] [ k ] + = ∑ t = L [ j ] − 1 i − 2 f [ t ] [ j − 1 ] [ k − 1 ] f[i][j][k]+=\sum_{t=L[j]-1}^{i-2}f[t][j-1][k-1] f[i][j][k]+=t=L[j]1i2f[t][j1][k1]

那个 ∑ \sum 就是一个前缀和,用一个sum数组存一下就好了,复杂度 O ( n 3 ) O(n^3) O(n3)

Code:

#include<bits/stdc++.h>
#define maxn 205
const int mod = 1e9+7;
using namespace std;
int T,n,K,a[maxn],L[maxn],R[maxn];
int f[maxn][maxn][maxn],sum[maxn][maxn][maxn];
inline int getsum(int i,int j,int k){
	return (i<0||k<0)?0:sum[i][j][k];
}
int main()
{
	//freopen("permutation.in","r",stdin);
	//freopen("permutation.out","w",stdout);
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&K);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		for(int i=1;i<=n;i++){
			L[i]=R[i]=i;
			while(L[i]>1&&a[L[i]-1]<a[i]) L[i]--;
			while(R[i]<n&&a[R[i]+1]<a[i]) R[i]++;
		}
		for(int j=0;j<=n;j++) f[0][j][0]=sum[0][j][0]=1;
		for(int i=1;i<=n;i++)
			for(int j=0;j<=n;j++)
				for(int k=0;k<=K;k++){
					if(j){
						f[i][j][k]=f[i][j-1][k];
						if(L[j]<=i&&i<=R[j]){
							if(k-(i!=j)>=0) f[i][j][k]=(f[i][j][k]+f[i-1][j-1][k-(i!=j)])%mod;
							f[i][j][k]=(1ll*f[i][j][k]+getsum(i-2,j-1,k-1)-getsum(L[j]-2,j-1,k-1))%mod;
							//        1ll!!!!....
						}
					}//attention j from 0 to n cause ↓
					sum[i][j][k]=(sum[i-1][j][k]+f[i][j][k])%mod;
				}
		
		int ans=0;
		for(int i=0;i<=K;i++) ans=(ans+f[n][n][i])%mod;
		printf("%d\n",(ans+mod)%mod);
	}
}
T3 方阵计数

在这里插入图片描述
n ≤ 1 0 10 n\le10^{10} n1010,时限5s

题目分析:

首先你需要打开百度百科:
在这里插入图片描述
然后枚举正方形的边长 i i i,斜的相当于一个顶点在边上滑动,设滑动长为 j j j
面积 S = j 2 + ( i − j ) 2 S=j^2+(i-j)^2 S=j2+(ij)2,边界点数 = 4 g c d ( i , j ) =4gcd(i,j) =4gcd(i,j)。所以
A n s = ∑ i = 1 n ( n − i + 1 ) 2 ∗ ∑ j = 1 i j 2 + ( i − j ) 2 + 2 g c d ( i , j ) + 1 Ans=\sum_{i=1}^n(n-i+1)^2*\sum_{j=1}^ij^2+(i-j)^2+2gcd(i,j)+1 Ans=i=1n(ni+1)2j=1ij2+(ij)2+2gcd(i,j)+1
把Ans分成常数部分和gcd部分:
A n s 1 = ∑ i = 1 n ( n − i + 1 ) 2 ∑ j = 1 i j 2 + ( i − j ) 2 + 1 = ∑ i = 1 n ( i 2 − 2 ( n + 1 ) i + ( n + 1 ) 2 ) 2 ( i 3 + 2 i ) 3 = ∑ i = 1 n 2 3 i 5 − 4 ( n + 1 ) 3 i 4 + 2 ( n + 1 ) 2 + 4 3 i 3 − 8 ( n + 1 ) 3 i 2 + 4 ( n + 1 ) 2 3 i = 2 3 ∑ i = 1 n i 5 − 2 ( n + 1 ) i 4 + ( ( n + 1 ) 2 + 2 ) i 3 − 4 ( n + 1 ) i 2 + 2 ( n + 1 ) 2 i \begin{aligned} Ans_1&amp;=\sum_{i=1}^n(n-i+1)^2\sum_{j=1}^ij^2+(i-j)^2+1\\ &amp;=\sum_{i=1}^n\left(i^2-2(n+1)i+(n+1)^2\right)\frac {2(i^3+2i)}3\\ &amp;=\sum_{i=1}^n\frac 23i^5-\frac {4(n+1)}3i^4+\frac {2(n+1)^2+4}3i^3-\frac {8(n+1)}3i^2+\frac {4(n+1)^2}3i\\ &amp;=\frac 23\sum_{i=1}^ni^5-2(n+1)i^4+((n+1)^2+2)i^3-4(n+1)i^2+2(n+1)^2i\end{aligned} Ans1=i=1n(ni+1)2j=1ij2+(ij)2+1=i=1n(i22(n+1)i+(n+1)2)32(i3+2i)=i=1n32i534(n+1)i4+32(n+1)2+4i338(n+1)i2+34(n+1)2i=32i=1ni52(n+1)i4+((n+1)2+2)i34(n+1)i2+2(n+1)2i
A n s 1 Ans_1 Ans1用自然数幂和公式可 O ( 1 ) O(1) O(1)算出。请自行百度
A n s 2 = ∑ i = 1 n ( n − i + 1 ) 2 ∑ j = 1 i 2 g c d ( i , j ) = 2 ( ∑ d = 1 n d ∑ i = 1 n d ( n − i d + 1 ) 2 ∑ j = 1 i [ ( i , j ) = = 1 ] ) = 2 ( ∑ d = 1 n d ∑ i = 1 n d ( n − i d + 1 ) 2 φ ( i ) ) = 2 ( ∑ d = 1 n d ∑ i = 1 n d ( d 2 i 2 − 2 ( n + 1 ) d i + ( n + 1 ) 2 ) φ ( i ) ) = 2 ( ∑ d = 1 n d 3 ∑ i = 1 n d φ ( i ) ∗ i 2 − 2 ( n + 1 ) ∑ d = 1 n d 2 ∑ i = 1 n d φ ( i ) ∗ i + ( n + 1 ) 2 ∑ d = 1 n d ∑ i = 1 n d φ ( i ) ) \begin{aligned} Ans_2&amp;=\sum_{i=1}^n(n-i+1)^2\sum_{j=1}^i2gcd(i,j)\\ &amp;=2\left(\sum_{d=1}^nd\sum_{i=1}^{\frac nd}(n-id+1)^2\sum_{j=1}^i[(i,j)==1]\right)\\ &amp;=2\left(\sum_{d=1}^nd\sum_{i=1}^{\frac nd}(n-id+1)^2\varphi(i)\right)\\ &amp;=2\left(\sum_{d=1}^nd\sum_{i=1}^{\frac nd}(d^2i^2-2(n+1)di+(n+1)^2)\varphi(i)\right)\\ &amp;=2\left(\sum_{d=1}^nd^3\sum_{i=1}^{\frac nd}\varphi(i)*i^2-2(n+1)\sum_{d=1}^nd^2\sum_{i=1}^{\frac nd}\varphi(i)*i+(n+1)^2\sum_{d=1}^nd\sum_{i=1}^{\frac nd}\varphi(i)\right) \end{aligned} Ans2=i=1n(ni+1)2j=1i2gcd(i,j)=2d=1ndi=1dn(nid+1)2j=1i[(i,j)==1]=2d=1ndi=1dn(nid+1)2φ(i)=2d=1ndi=1dn(d2i22(n+1)di+(n+1)2)φ(i)=2d=1nd3i=1dnφ(i)i22(n+1)d=1nd2i=1dnφ(i)i+(n+1)2d=1ndi=1dnφ(i)
A n s 2 Ans_2 Ans2需要求出 φ ( i ) ∗ i 2 , φ ( i ) ∗ i , φ ( i ) \varphi(i)*i^2,\varphi(i)*i,\varphi(i) φ(i)i2,φ(i)i,φ(i)的前缀和,用杜教筛在 O ( n 2 3 ) O(n^{\frac 23}) O(n32)可算出。
φ ( i ) ⋅ i \varphi(i)\cdot i φ(i)i为例: ( φ ⋅ i d ) ∗ i d = ∑ i ∣ n φ ( i ) ⋅ i ⋅ n i = n ∑ i ∣ n φ ( i ) = i d 2 (\varphi\cdot id)*id=\sum_{i|n}\varphi(i)\cdot i\cdot \frac ni=n\sum_{i|n}\varphi(i)=id^2 (φid)id=inφ(i)iin=ninφ(i)=id2
所以 ∑ i = 1 n i 2 = ∑ i = 1 n ∑ d ∣ i φ ( d ) ⋅ d ⋅ i d = ∑ i = 1 n i ∑ d = 1 ⌊ n i ⌋ φ ( d ) ⋅ d = ϕ ( n ) + ∑ i = 2 n ϕ ( ⌊ n i ⌋ ) \sum_{i=1}^ni^2=\sum_{i=1}^n\sum_{d|i}\varphi(d)\cdot d\cdot \frac id=\sum_{i=1}^ni\sum_{d=1}^{\lfloor\frac ni\rfloor}\varphi(d)\cdot d=\phi(n)+\sum_{i=2}^n\phi(\lfloor\frac ni\rfloor) i=1ni2=i=1ndiφ(d)ddi=i=1nid=1inφ(d)d=ϕ(n)+i=2nϕ(in)

Code:

#include<bits/stdc++.h>
#define maxn 10000005
#define LL long long
using namespace std;
const int N = maxn-5;
const int mod = 998244353, inv2 = 499122177, inv3 = 332748118;
const int inv6 = 166374059, inv30 = 432572553, inv12 = 582309206;
int sum[maxn],p[maxn/5],phi0[maxn],phi1[maxn],phi2[maxn];
bool v[maxn];
map<LL,int>F0,F1,F2;
void Prime(int N){
	phi0[1]=1; int m=0;
	for(int i=2;i<=N;i++){
		if(!v[i]) p[++m]=i,phi0[i]=i-1;
		for(int j=1,k;j<=m&&(k=p[j]*i)<=N;j++){
			v[k]=1;
			if(i%p[j]==0) {phi0[k]=phi0[i]*p[j];break;}
			phi0[k]=phi0[i]*phi0[p[j]];
		}
	}
	for(int i=1;i<=N;i++){
		phi2[i]=(phi2[i-1]+1ll*phi0[i]*i%mod*i)%mod;
		phi1[i]=(phi1[i-1]+1ll*phi0[i]*i)%mod;
		phi0[i]=(phi0[i-1]+phi0[i])%mod;
	}
}
inline LL sqr(LL x){return x*x%mod;}
namespace Sum{
	int _1(LL n){n%=mod;return n*(n+1)/2%mod;}
	int _2(LL n){n%=mod;return n*(n+1)%mod*(2*n+1)%mod*inv6%mod;}
	int _3(LL n){n%=mod,n=n*(n+1)/2%mod;return sqr(n);}
	int _4(LL n){n%=mod;return n*(n+1)%mod*(6*(sqr(n)*n)%mod+9*sqr(n)+n-1)%mod*inv30%mod;}
	int _5(LL n){n%=mod;return sqr(n*(n+1)%mod)*(2*sqr(n)+2*n-1)%mod*inv12%mod;}
	int Sub(LL l,LL r,int k){
		switch(k){
			case 1: return (_1(r)-_1(l-1)+mod)%mod;
			case 2: return (_2(r)-_2(l-1)+mod)%mod;
			case 3: return (_3(r)-_3(l-1)+mod)%mod;
		}
	}
}
LL n;
int Phi(LL n){
	if(n<=N) return phi0[n];
	if(F0.count(n)) return F0[n];
	int ret=Sum::_1(n);
	for(LL i=2,j;i<=n;i=j+1)
		j=n/(n/i),ret=(ret-(j-i+1)*Phi(n/i))%mod;
	return F0[n]=ret;
}
int Phi_i(LL n){
	if(n<=N) return phi1[n];
	if(F1.count(n)) return F1[n];
	int ret=Sum::_2(n);
	for(LL i=2,j;i<=n;i=j+1)
		j=n/(n/i),ret=(ret-1ll*Sum::Sub(i,j,1)*Phi_i(n/i))%mod;
	return F1[n]=ret;
}
int Phi_i_i(LL n){
	if(n<=N) return phi2[n];
	if(F2.count(n)) return F2[n];
	int ret=Sum::_3(n);
	for(LL i=2,j;i<=n;i=j+1)
		j=n/(n/i),ret=(ret-1ll*Sum::Sub(i,j,2)*Phi_i_i(n/i))%mod;
	return F2[n]=ret;
}
int solve1(LL n){
	int ret=0;
	for(LL i=1,j;i<=n;i=j+1)
		j=n/(n/i),ret=(ret+1ll*Sum::Sub(i,j,1)*Phi(n/i))%mod;
	return ret;
}
int solve2(LL n){
	int ret=0;
	for(LL i=1,j;i<=n;i=j+1)
		j=n/(n/i),ret=(ret+1ll*Sum::Sub(i,j,2)*Phi_i(n/i))%mod;
	return ret;
}
int solve3(LL n){
	int ret=0;
	for(LL i=1,j;i<=n;i=j+1)
		j=n/(n/i),ret=(ret+1ll*Sum::Sub(i,j,3)*Phi_i_i(n/i))%mod;
	return ret;
}
int main()
{
	//freopen("puzzle.in","r",stdin);
	//freopen("puzzle.out","w",stdout);
	scanf("%lld",&n);
	Prime(min(n,(LL)N));
	LL N=n%mod;
	int Ans1=2*(Sum::_5(n)-2*(N+1)*Sum::_4(n)+(sqr(N+1)+2)*Sum::_3(n)-4*(N+1)*Sum::_2(n)+2*sqr(N+1)*Sum::_1(n))%mod*inv3%mod;
	int Ans2=2*(solve3(n)-2*(N+1)*solve2(n)+sqr(N+1)*solve1(n))%mod;
	printf("%d\n",((Ans1+Ans2)%mod+mod)%mod);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值