f
(
i
,
j
,
k
)
f(i, j, k)
f(i,j,k):k = 0 表示存在一个圆,左端点为i,右端点为j,在
[
i
,
j
]
[i,j]
[i,j] 这个范围内随便放圆的方案数;k = 1 表示不存在一个左端点为 i,右端点为 j,在
[
i
,
j
]
[i,j]
[i,j] 这个范围内随便放圆的方案数。注意这里只考虑
[
i
,
j
]
[i,j]
[i,j] 这个范围内的方案数,并不算在这个范围之外怎么放圆。
那么,
f
(
i
,
j
,
1
)
f(i, j, 1)
f(i,j,1) 不为0只有一种情况,就是外边套圆,且
(
j
−
i
)
≤
10
(j - i)\le10
(j−i)≤10 且
(
j
−
i
)
%
2
=
=
0
(j - i) \% 2 == 0
(j−i)%2==0,这时候
f
(
i
,
j
,
1
)
=
f
(
i
,
j
,
0
)
f(i, j, 1) = f(i, j, 0)
f(i,j,1)=f(i,j,0)
接下来,讨论
f
(
i
,
j
,
0
)
f(i, j, 0)
f(i,j,0),如果 i 这个点不放一个圆(左端点为 i),结果就是
f
(
i
+
1
,
j
,
0
)
+
f
(
i
+
1
,
j
,
1
)
f(i + 1, j, 0) + f(i + 1, j, 1)
f(i+1,j,0)+f(i+1,j,1);如果i 这个点不放一个圆(左端点为 i),设这个圆的右端点为 r,那么就是看作
f
(
i
,
r
,
1
)
∗
[
f
(
r
,
j
,
1
)
+
f
(
r
,
j
,
0
)
]
f(i, r, 1) * [f(r, j, 1) + f(r, j, 0)]
f(i,r,1)∗[f(r,j,1)+f(r,j,0)].
#include<iostream>#include<algorithm>#include<cstring>usingnamespace std;constint maxn =1010, maxk =5010;//typedef pair<int, int> P;typedeflonglong ll;const ll mod =1e9+7;//P a[maxk];
ll f[maxn][maxn][2];//exist(i, j) 已经存在一个左端点为 i,右端点为 j 的圆;no(i, j) 表示不可以放一个左端点为 i,右端点为 j 的圆bool exist[maxn][maxn], no[maxn][maxn];intmain(){int N, K;scanf("%d%d",&N,&K);for(int i =0; i < K; i++){int C, R;scanf("%d%d",&C,&R);//该圆的左右边界int LL = C - R, RR = C + R;
exist[LL][RR]=true;//如果区间跨过该圆的一个边界,那么在这个区间内不能随便放圆for(int r = LL +1; r <= RR -1; r++){for(int l =0; l <= LL -1; l++) no[l][r]=true;}for(int l = LL +1; l <= RR -1; l++){for(int r = RR +1; r <= N; r++) no[l][r]=true;}}for(int l =0; l < N; l++){
f[l][l +1][0]=1;//空白也算一种方案
f[l][l +1][1]=0;//不存在直径为1的圆}for(int len =2; len <= N; len++){for(int l =0; l <= N - len; l++){int r = l + len;//no[l][r] 表示区间 [l, r] 内不可以随便放圆if(no[l][r]){
f[l][r][0]= f[l][r][1]=0;continue;}//l点不放圆
f[l][r][0]=(f[l +1][r][0]+ f[l +1][r][1])% mod;//l点放圆for(int D =2; D <=10&& D < len; D +=2){
f[l][r][0]=(f[l][r][0]+ f[l][l + D][1]*(f[l + D][r][0]+ f[l + D][r][1]))% mod;}if((r - l)%2==0&&(r - l)<=10){
f[l][r][1]= f[l][r][0];}if(exist[l][r]) f[l][r][0]=0;}}
ll ans =(f[0][N][0]+ f[0][N][1])% mod;printf("%lld\n", ans);return0;}
C. Ragdoll
启发式合并,挖坑。
D. Coordinate Paper
让你构造一个 n 的序列,要么
a
i
=
a
i
−
1
+
1
a_i=a_{i-1}+1
ai=ai−1+1。要么
a
i
+
1
=
a
i
−
k
a_{i+1}=a_{i}-k
ai+1=ai−k。和为
S
S
S
我们想,确定了
a
1
a_1
a1 之后,
a
2
a_2
a2要么是
a
1
+
1
a_1+1
a1+1,要么是
a
1
−
k
a_1-k
a1−k;这两种情况下,
(
a
1
+
a
2
)
%
(
k
+
1
)
(a_1+a_2) \% (k+1)
(a1+a2)%(k+1) 的值都是相等的。因此我们可以得到:当
a
1
a_1
a1 确定,整个序列的和
s
u
m
%
(
k
+
1
)
sum \%(k+1)
sum%(k+1) 也就确定了。这样我们可以从 0 到 k 枚举
a
1
a_1
a1 的值,然后
a
i
+
1
=
(
a
i
+
1
)
%
(
k
+
1
)
a_{i + 1} = (a_{i}+1)\%(k+1)
ai+1=(ai+1)%(k+1) 这样构造整个序列。如果
s
u
m
=
=
S
%
(
k
+
1
)
sum == S\%(k+1)
sum==S%(k+1) 且
S
>
=
s
u
m
S >= sum
S>=sum,那么这种方案就可以。如果没有方案满足要求就输出-1。
接下来考虑如何将答案变大,我们可以先把 0 变成
k
+
1
k+1
k+1;如果 0 用完了可以将 1 变成
k
+
2
k+2
k+2 …由此得到最后的总和
S
S
S 。
细节真多
#include<iostream>#include<algorithm>#include<cstring>#include<vector>usingnamespace std;typedeflonglong ll;constint maxn =100010;typedef pair<ll, ll> P;
ll a[maxn];intmain(){
ll N, K, S;scanf("%lld%lld%lld",&N,&K,&S);
ll sum =0;for(int i =1; i <= N; i++){
a[i]= i %(K +1);
sum += a[i];}
ll a1 = a[1], an = a[N];for(int i =0; i <= K; i++){if((sum %(K +1)== S %(K +1))&& S >= sum){
vector<P> ids;//记录每个数字出现的下标//不要忘记将此数组更新for(int j =1; j <= N; j++){
a[j]=(a[j]+ i)%(K +1);
ids.push_back({ a[j], j });}sort(ids.begin(), ids.end());
ll u =(S - sum +(N *(K +1)-1))/(N *(K +1)), v =(S - sum)%(N *(K +1))/(K +1);//printf("*** %lld %lld %lld %lld %lld\n", a1, an, sum, u, v);//细节,小心 u 可能等于0if(u !=0){//如果 v 为 0 的话,试试 2 1 5 这个样例就明白了if(v ==0) u++;for(auto p : ids){
ll id = p.second;if(v){
a[id]+= u *(K +1);
v--;}else{
a[id]+=(u -1)*(K +1);}}}for(int j =1; j <= N; j++){printf("%lld%c", a[j], j == N ?'\n':' ');}return0;}
sum = sum - a1 +(an +1)%(K +1);
a1 =(a1 +1)%(K +1), an =(an +1)%(K +1);}printf("-1\n");return0;}
2021 BNU Winter Training 6 (The 14th Chinese Northeast Collegiate Programming Contest)训练网址A. Strange MemoryB. Abstract PaintingC. RagdollD. Coordinate Paper