1696. Salary for Robots
Time limit: 2.0 second
Memory limit: 64 MB
Memory limit: 64 MB
There are
n robots on planet PTZZZ. Each robot has its own unique rank—an integer from 1 to
n, and should execute all orders from robots with a higher rank.
Once a month all robots get their salary: a positive integer number of credits, not exceeding
k. The salary is paid by an accountant-robot. Salary is so important for robots that the first month when all the robots got their salary was named the First month of the First year. There are
p months in the year on PTZZZ, so the robots get their salary
p times a year.
The salary paid to each robot can be different in different months. If it turns out that all the robots get exactly the same salary as in any month earlier, the accountant-robot will rust of sadness. What is more, the law doesn't allow the accountant-robot to pay salary in such a way that there will be a triple of robots (
a,
b,
c) with rank of
a higher than rank of
b, rank of
b higher than rank of
c and the salary of
a less than the salary of
b and the salary of
b less than the salary of
c.
The accountant-robot doesn't want to rust, so since the First month of the First year he tries to pay salary in different ways. However, the accountant-robot will rust sooner or later. Your task is to calculate the month number when this will happen.
Input
The only input line contains three space-separated integers
n,
k and
p—the number of robots on PTZZZ, the maximal possible salary and the number of months in a year, respectively
(1 ≤
n ≤ 1000; 1 ≤
k ≤ 200; 2 ≤
p ≤ 10
9)
.
Output
Output the month number the accountant-robot will rust in. Months are numerated 1 to
p.
Sample
input | output |
---|---|
3 3 20 | 7 |
Problem Author: Igor Chevdar
Problem Source: Ural SU Contest. Petrozavodsk Winter Session, February 2009
Problem Source: Ural SU Contest. Petrozavodsk Winter Session, February 2009
Tags:
dynamic programming
)
Difficulty: 872
Printable version
Submit solution
Discussion (8)
All submissions (2219) All accepted submissions (495) Solutions rating (333)
All submissions (2219) All accepted submissions (495) Solutions rating (333)
题目大意:给定一个数列a[1]——a[n],1≤a[i]≤k,数列中不能出现长度为3的严格递减子序列,求序列个数。
思路:此题综合了DP的所有难点:状态不好想,转移不好想,优化不好想,代码不好写。。。
状态:此处给出老师讲的
我历尽千辛万苦想出来的
思考过程:乍一看很不好像,我们要分析限制的量,从而来设置状态维度。我们设想已知前n-1个数,求第n个数,可以选取哪些数?很明显这取决于前面的递减二元组(若i<j,a[i]>a[j])的第二元素的最大值,因为若该元素小于那个最大值,我们就能构成一个严格下降的子序列。所以我们可以构造状态:f[i][j][k]表示前i个数,最大值为m,所有递减二元组中第二个元素的最大值为k。此状态的复杂度为O(nk²).
转移:很容易想出最简单的转移:枚举j,k,f[i][j][k]=f[i-1][1..K][1..K](此处K为最大薪水)。转移复杂度O(k²),总复杂度O(nk^4).思考优化。
我们考虑a[n]的取值,若a[n]<k,明显不成立,因为那样就能构成一个递减子序列。若k<a[n]<m,也不成立,因为那样会违背k的定义,a[n]就一定可以成为递减二元组的次大值。若a[n]>m,更不成立,直接违背m的定义。所以综上,我们发现了一个神奇的事情!a[n]只能取值m或k,这样就可以省去一重枚举,复杂度O(nk³),再使用前缀和优化,可以省去一重转移,复杂度O(nk²)。
附代码。
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<ctime>
#define INF 0x3f3f3f3f
using namespace std;
const int MAXN=1010;
const int MAXK=210;
int f[2][MAXK][MAXK];
int suma[MAXK][MAXK],sumb[MAXK][MAXK];
int n,k,p;
void getsum(int pos)
{
for (int i=0;i<=k;i++)
{
suma[i][0]=f[pos][i][0];
for (int j=1;j<=k;j++)
{
suma[i][j]=0;
suma[i][j]=suma[i][j-1]+f[pos][i][j];
if (suma[i][j]>p)
{
suma[i][j]-=p;
}
}
}
for (int i=0;i<=k;i++)
{
sumb[i][0]=f[pos][0][i];
for (int j=1;j<=k;j++)
{
sumb[i][j]=0;
sumb[i][j]=sumb[i][j-1]+f[pos][j][i];
if (sumb[i][j]>p)
{
sumb[i][j]-=p;
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&k,&p);
f[0][1][0]=1;
getsum(0);
long long ans=0;
for (int ii=1;ii<=n;ii++)
{
int i=ii&1;
for (int j=1;j<=k;j++)
{
for (int l=0;l<j;l++)
{
f[i][j][l]=sumb[l][j]-sumb[l][l];
if (l)
{
f[i][j][l]+=suma[j][l];
}
if (f[i][j][l]>p)
{
f[i][j][l]-=p;
}
}
}
getsum(i);
}
for (int i=1;i<=k;i++)
{
for (int j=0;j<i;j++)
{
ans+=f[n%2][i][j];
ans%=p;
}
}
cout<<ans+1;
return 0;
}