NOIP 2011 提高组
计算系数
题目描述
给定一个多项式(by+ax)k,请求出多项式展开后xn * ym 项的系数。
输入
共一行,包含5 个整数,分别为 a ,b ,k ,n ,m,每两个整数之间用一个空格隔开。0≤k≤1000, 0≤n,m≤k 且 n+m=k, 0≤a,b≤100,000
输出
输出共1 行,包含一个整数,表示所求的系数,这个系数可能很大,输出对10007 取模后的结果。
样例输入
1 1 3 1 2
样例输出
3
思路
通过公式
借用杨辉三角,即可推出答案
代码实现
#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
#include <cmath>
#include <cctype>
#include <vector>
#include <algorithm>
#include <map>
#include <stack>
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int mod=10007;
const int N=10005;
ll s[N][N];
ll qpow(ll k,ll n)
{
ll ans=1;
while(n>0)
{
if(n%2!=0) ans=ans*k%mod;
k=k*k%mod;
n/=2;
}
return ans;
}
int main()
{
int a,b,k,n,m;
scanf("%d%d%d%d%d",&a,&b,&k,&n,&m);
s[1][1]=1;
for(int i=2;i<=k+1;i++)
{
for(int j=1;j<=i;j++) s[i][j]=s[i-1][j-1]%mod+s[i-1][j]%mod;
}
cout<<s[k+1][k-n+1]*qpow(a,n)*qpow(b,m)%mod<<endl;
return 0;
}
选择客栈
题目描述
丽江河边有 n 家很有特色的客栈,客栈按照其位置顺序从 1 到n 编号。每家客栈都按照某一种色调进行装饰(总共 k 种,用整数 0 ~ k-1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。
两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过 p。
他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 p元的咖啡店小聚。
输入
输入共n+1行。
第一行三个整数 n,k,p,每两个整数之间用一个空格隔开,分别表示客栈的个数,色调的数目和能接受的最低消费的最高值;
接下来的 n行,第 i+1 行两个整数,之间用一个空格隔开,分别表示 i 号客栈的装饰色调和 i 号客栈的咖啡店的最低消费。
2 ≤n ≤200,000,0<k ≤50,0≤p ≤100 , 0 ≤最低消费≤100。
输出
输出只有一行,一个整数,表示可选的住宿方案的总数。
样例输入
5 2 3
0 5
1 3
0 2
1 4
1 5
样例输出
3
提示
输入输出样例说明:
客栈编号 1 2 3 4 5
色调 0 1 0 1 1
最低消费 5 3 2 4 5
2 人要住同样色调的客栈,所有可选的住宿方案包括:住客栈①③,②④,②⑤,④⑤,
但是若选择住 4、5 号客栈的话,4、5 号客栈之间的咖啡店的最低消费是 4,而两人能承受
的最低消费是 3 元,所以不满足要求。因此只有前 3 种方案可选。
思路:
从右向左看,如果其左边又符合条件的咖啡店,并且存在相同颜色的客栈,则方案数加一,那么如果以这个右边的旅店作为对应点,将所有在左边而且颜色与之相
同的旅店数相加,就能得出很多种住宿方法了。那么用这个办法,用所有的对应点对应过去,就能最快的时间内找出所用的酒店了。
代码实现
#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
#include <cmath>
#include <cctype>
#include <vector>
#include <algorithm>
#include <map>
#include <stack>
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int mod=10007;
const int N=2000005;
int t[N],last[N],num[N];
int ans,now;
int main()
{
int n,k,p;
scanf("%d%d%d",&n,&k,&p);
for(int i=0;i<n;i++)
{
int co,mo;
scanf("%d%d",&co,&mo);
if(mo<=p) now=i; //在次数为i时,在now限度下是否有可用咖啡店
if(now>=last[co]) t[co]=num[co];//若在可用咖啡店,则左边的所有颜色相同的客栈都可用
num[co]++;//颜色为co的客栈数+1
ans+=t[co];
last[co]=i;//记录最后一次遇见颜色为co的客栈的位置
}
printf("%d\n",ans);
return 0;
}
NOIP 2012 普及组
寻宝
题目描述
传说很遥远的藏宝楼顶层藏着诱人的宝藏。小明历尽千辛万苦终于找到传说中的这个藏宝楼,藏宝楼的门口竖着一个木板,上面写有几个大字:寻宝说明书。说明书的内容如下:
藏宝楼共有N+1层,最上面一层是顶层,顶层有一个房间里面藏着宝藏。除了顶层外,藏宝楼另有N层,每层M个房间,这M个房间围成一圈并按逆时针方 向依次编号为0,…,M-1。其中一些房间有通往上一层的楼梯,每层楼的楼梯设计可能不同。每个房间里有一个指示牌,指示牌上有一个数字x,表示从这个房 间开始按逆时针方向选择第x个有楼梯的房间(假定该房间的编号为k),从该房间上楼,上楼后到达上一层的k号房间。比如当前房间的指示牌上写着2,则按逆 时针方向开始尝试,找到第2个有楼梯的房间,从该房间上楼。如果当前房间本身就有楼梯通向上层,该房间作为第一个有楼梯的房间。
寻宝说明书的最后用红色大号字体写着:“寻宝须知:帮助你找到每层上楼房间的指示牌上的数字(即每层第一个进入的房间内指示牌上的数字)总和为打开宝箱的密钥”。
请帮助小明算出这个打开宝箱的密钥。
输入
第一行2个整数N和M,之间用一个空格隔开。N表示除了顶层外藏宝楼共N层楼,M表示除顶层外每层楼有M个房间。
接下来N*M行,每行两个整数,之间用一个空格隔开,每行描述一个房间内的情况,其中第(i-1)*M+j行表示第i层j-1号房间的情况 (i=1,2,…, N;j=1,2,…,M)。第一个整数表示该房间是否有楼梯通往上一层(0表示没有,1表示有),第二个整数表示指示牌上的数字。注意,从j号房间的楼梯 爬到上一层到达的房间一定也是j号房间。
最后一行,一个整数,表示小明从藏宝楼底层的几号房间进入开始寻宝(注:房间编号从0开始)。
0<N≤10000,0<M≤100,0<x≤1,000,000
输出
输出只有一行,一个整数,表示打开宝箱的密钥,这个数可能会很大,请输出对20123取模的结果即可。
样例输入
2 3
1 2
0 3
1 4
0 1
1 5
1 2
1
样例输出
5
思路:
模拟小明上楼的过程,在输入数据是记录每一层有楼梯房间的个数,以提高效率
代码实现
#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
#include <cmath>
#include <cctype>
#include <vector>
#include <algorithm>
#include <map>
#include <stack>
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int mod=20123;
const int N=10005;
bool lt[N][105];
int c[N],ro[N][105];
int ans;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=0;j<m;j++)
{
int temp;
scanf("%d%d",&temp,&ro[i][j]);
if(temp)
{
c[i]++;
lt[i][j]=true;
}
}
}
int now;
scanf("%d",&now);
for(int i=1;i<=n;i++)
{
ans+=ro[i][now];
ans%=mod;
if(i==n) break;
int temp=ro[i][now]%c[i];
if(!temp) temp=c[i];
for(int j=1;j<=temp;j++)
{
while(!lt[i][now])
{
now++;
now%=m;
}
if(j==temp) break;
now++;
now%=m;
}
}
printf("%d\n",ans%mod);
return 0;
}
摆花
题目描述
小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆。通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号。为了在门口展出更多种花,规定第i种花不能超过ai盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。
输入
第一行包含两个正整数n和m,中间用一个空格隔开。
第二行有n个整数,每两个整数之间用一个空格隔开,依次表示a1、a2、……an。
0<n≤100,0<m≤100,0≤ ai≤100
输出
输出只有一行,一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对1000007取模的结果。
样例输入
2 4
3 2
样例输出
2
思路:
用DP的方法,由题可得状态转移方程
dp[i][j]=dp[i-1][j]+dp[i-1][j-1]……+dp[i-1][j-a[i]]
代码实现
#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
#include <cmath>
#include <cctype>
#include <vector>
#include <algorithm>
#include <map>
#include <stack>
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int mod=1000007;
const int N=105;
int a[N];
int dp[N][N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int k=0;k<=m;k++)
{
for(int j=0;j<=a[i] && j<=k;j++)
{
dp[i][k]+=dp[i-1][k-j];
dp[i][k]%=mod;
}
}
}
printf("%d\n",dp[n][m]);
return 0;
}