题目
Description
xc抽空光顾了lp的饲养场,在一大堆赞美语之后和lp玩起了一个游戏——
一个完整的倒三角有n层,第一层有n个数字,为原始数字,接下来每层都比上一层减少1个数字,并有f[i,j]=f[i-1,j]+f[i-1,j+1] ,如
3 1 2 4
4 3 6
7 9
16
由xc给出f[n,1],和一个限制max(0<=f[1,i]<=max),lp要迅速回答出字典序最小的f[1]序列,即f[1,1]最小的情况下f[1,2]要最小……以此类推。
xc觉得这个问题对于lp还是太简单了,于是又添加了一些坏点,以坐标的形式给出,所谓坏点就是f[i,j]恒为0,现在就让大家一起来玩这个游戏吧。
Input
第一行四个数 n,m,max,f[n,1]
接下来m行,每行一个坐标,表示是一个坏点
Output
无解则输出-1
否则输出n行,第i行输出f[1,i],要求f[1]字典序最小
Sample Input
3 1 3 4
2 2
Sample Output
1
3
0
Hint
【数据说明】
40%的数据:n<=10;m=1;max<=10;f[n,1]<=1,000
90%的数据:max<=100
100%的数据:n<=100;m<=20;max<=10,000;0<=f[n,1]<=10,00
分析
观察这个三角形的组成方式,有点像杨辉三角。
那我们先不考虑存在坏点的情况,
设 fi,j 表示i,j这个位置的数值
从第n行向前推:
fn,1=fn,1
向上一行:
fn,1=fn−1,1+fn−1,2
继续推:
fn,1=fn−1,1+fn−1,2=fn−2,1+2∗fn−2,2+fn−2,3
fn,1=fn−1,1+fn−1,2=fn−2,1+2∗fn−2,2+fn−2,3=fn−3,1+3∗fn−3,2+3∗fn−3,3+fn−3,4
观察每一项的系数,我们就可以发现规律,系数就是杨辉三角。
我们就用 gi,j 表示它对应的 fi,j 的系数。
现在,再重新去考虑坏点的情况:
因为坏点要求这个点始终是0,
所以,如果这个点是坏点,那么就直接把 gi,j 赋值等于0就可以了。
到了最后我们可以得到一个多元一次方程:
fn,1=∑ni=1g1,i∗f1,i
- 接下来的事情就是解方程了。
我们只需要求一组整数解,而且是字典序最小的,所以不需要什么高级方法。
就是递归求解,加上一下玄学的剪枝优化就可以了。
Code(c++)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <stdlib.h>
#include <math.h>
using namespace std;
struct arr{
int x,y;
}a[30];
int n,m,mx,p,x,ans;
int f[2][103];
bool pd;
int g[130];
bool cmp(arr x,arr y)
{
return (x.x>y.x)||((x.x==y.x)&&(x.y<y.y));
}
void dg(int x,int sum)
{
if(pd)return;
if(x>n)
{
if(sum==ans)pd=1;
return;
}
if(f[p][x]==0)dg(x+1,sum);
else
{
for(int i=0;i<=min((ans-sum)/f[p][x],mx);i++)
{
g[x]=i;
dg(x+1,sum+f[p][x]*i);
if(pd)return;
}
}
}
int main()
{
p=0;
scanf("%d%d%d%d",&n,&m,&mx,&ans);
for(int i=1;i<=m;i++)
scanf("%d%d",&a[i].x,&a[i].y);
f[0][1]=1;
sort(a+1,a+1+m,cmp);
x=1;
for(int i=2;i<=n;i++)
{
p=1-p;
memset(f[p],0,sizeof(f[p]));
for(int j=1;j<=i;j++)
{
f[p][j]+=f[1-p][j];
f[p][j+1]+=f[1-p][j];
}
for(int j=1;j<=i;j++)
{
if((n-i+1==a[x].x)&&(j==a[x].y))
{
x++;
f[p][j]=0;
}
}
}
pd=0;
dg(1,0);
if(!pd)
{
printf("-1\n");
return 0;
}
for(int i=1;i<=n;i++)
printf("%d\n",g[i]);
}