T1 水池
n
,
m
,
L
≤
1000
n,m,L\le1000
n,m,L≤1000
题目分析
考虑积水从最低的高度慢慢往上涨,什么时候会流出去,记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
T≤10,n≤200
题目分析
这道题要是想什么按顺序从大到小放区间你就自闭了。。。
你会发现一个值覆盖的区间不一定要包含自己。。
发现两个答案序列不同当前仅当有一位不同,我们不去想操作是怎么一步一步做的,只需要知道答案序列长什么样子就好了,我们可以从答案序列推出用了多少次操作。
分析一下答案序列长什么样,一个值最后肯定一段连续的区间,不会分开(相当于自己覆盖一整段再被左右两边盖去一部分)。
顺着放序列的每一位,设
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][j−1][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[i−1][j−1][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[i−1][j−1][k−1]
- 第 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]−1i−2f[t][j−1][k−1]
那个 ∑ \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}
n≤1010,时限5s
题目分析:
首先你需要打开百度百科:
然后枚举正方形的边长
i
i
i,斜的相当于一个顶点在边上滑动,设滑动长为
j
j
j。
面积
S
=
j
2
+
(
i
−
j
)
2
S=j^2+(i-j)^2
S=j2+(i−j)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=1∑n(n−i+1)2∗j=1∑ij2+(i−j)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&=\sum_{i=1}^n(n-i+1)^2\sum_{j=1}^ij^2+(i-j)^2+1\\ &=\sum_{i=1}^n\left(i^2-2(n+1)i+(n+1)^2\right)\frac {2(i^3+2i)}3\\ &=\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\\ &=\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=1∑n(n−i+1)2j=1∑ij2+(i−j)2+1=i=1∑n(i2−2(n+1)i+(n+1)2)32(i3+2i)=i=1∑n32i5−34(n+1)i4+32(n+1)2+4i3−38(n+1)i2+34(n+1)2i=32i=1∑ni5−2(n+1)i4+((n+1)2+2)i3−4(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&=\sum_{i=1}^n(n-i+1)^2\sum_{j=1}^i2gcd(i,j)\\ &=2\left(\sum_{d=1}^nd\sum_{i=1}^{\frac nd}(n-id+1)^2\sum_{j=1}^i[(i,j)==1]\right)\\ &=2\left(\sum_{d=1}^nd\sum_{i=1}^{\frac nd}(n-id+1)^2\varphi(i)\right)\\ &=2\left(\sum_{d=1}^nd\sum_{i=1}^{\frac nd}(d^2i^2-2(n+1)di+(n+1)^2)\varphi(i)\right)\\ &=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=1∑n(n−i+1)2j=1∑i2gcd(i,j)=2⎝⎛d=1∑ndi=1∑dn(n−id+1)2j=1∑i[(i,j)==1]⎠⎞=2⎝⎛d=1∑ndi=1∑dn(n−id+1)2φ(i)⎠⎞=2⎝⎛d=1∑ndi=1∑dn(d2i2−2(n+1)di+(n+1)2)φ(i)⎠⎞=2⎝⎛d=1∑nd3i=1∑dnφ(i)∗i2−2(n+1)d=1∑nd2i=1∑dnφ(i)∗i+(n+1)2d=1∑ndi=1∑dnφ(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=∑i∣nφ(i)⋅i⋅in=n∑i∣nφ(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=1n∑d∣iφ(d)⋅d⋅di=∑i=1ni∑d=1⌊in⌋φ(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);
}