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;}