您的位置:首页 > 理论基础 > 计算机网络

“亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛 (部分题解)

2015-03-30 20:10 429 查看
“亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛

E

Dreaming

时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 84 测试通过 : 4

题目描述

我们定义一个数good当且仅当它只由a和b构成,且数位和sum各数位也仅由a和b构成。举个栗子:若a=1,b=2,那么13不是good,11是(都由a=1构成,数位和sum=2由b=2构成)。那么窝们定义一个数的长度为n,那么有多少个数是good呢?所求答案对10^9+7取模。

输入

多组样例。

每行包含三个数a,b,n(1<=a,b<=9,1<=n<=10^6)

输出

每组数据输出一个整数。

样例输入

1 3 3

样例输出

1

题目来源

NUPT

题解:

1.这题就是要求,使用i个a,n-i个b,如果满足条件,ans+=C(n,i);

2.这题n比较大,故直接枚举这个数比较耗时,可以改为枚举数位和sum,sum最大为 9*1e6,故复杂度大大下降。

3.n比较大,故C
[i]用数组存不下,直接递推也会超时,可以考虑用最基本的公式 C(n,k)=(n!)/(k!)/(n-k)! ,然后除法采用逆元操作。

可以使用公式 (a/b)%mod=a*b^(mod-2)%mod ,如果b与mod互质的话。 不过,小明哥给了一个更好的递推公式,见代码。

关于逆元详解:
http://blog.csdn.net/acdreamers/article/details/8220787
ll f
;        //n!
ll inv
;      //逆元
ll nf
;       //n!的逆元
int mi,ma;
ll ans;

void pre()
{
inv[0] = inv[1] = 1;
f[0] = f[1] = 1;
nf[0] = nf[1] = 1;
for (int i = 2; i < N; i++) {
inv[i] = ((mod - mod / i) * inv[mod % i]) % mod;
f[i] = (f[i - 1] * i)%mod;
nf[i] = (nf[i - 1] * inv[i])%mod;
}
}


思路来源于小明哥,先转一发小明哥的代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<cmath>
using namespace std;
int a[100005];
__int64 pmod = 1000000009;
__int64 inv[100005];
__int64 ba[100005];
__int64 rba[100005];
#define M 100005
void pre() {
inv[0] = inv[1] = 1;
ba[0] = ba[1] = 1;
rba[0] = rba[1] = 1;
for (int i = 2; i < M; i++) {
inv[i] = ((pmod - pmod / i) * inv[pmod % i]) % pmod;
ba[i] = (ba[i - 1] * i)%pmod;
rba[i] = (rba[i - 1] * inv[i])%pmod;
}
}
__int64 C(int n, int k) {
return (ba
* rba[k] % pmod )* rba[n - k] % pmod;
}
int main() {
int n, m;
int l, r;
int i;
int nl, nr;
pre();
while (scanf("%d%d", &n, &m) != EOF) {
for (i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
l = 0;
r = 1;
int o = 0;
for (i = 0; i < n; ++i) {
nl = min(abs(l - a[i]), abs(r - a[i]));
if (l <= a[i] && r >= a[i])
nl = 0;
nr = max(m - abs(l + a[i] - m), m - abs(r + a[i] - m));
if (l <= m - a[i] && r >= m - a[i])
nr = m;
o = (o + a[i]) % 2;
l = nl;
r = nr;
}
__int64 ans = 0;
for (i = l; i <= r; ++i) {
if (i % 2 == o) {
ans += C(m, i);
ans %= pmod;
}
}
printf("%I64d\n", ans);
}
}


View Code

[align=center]njczy2010[/align]
[align=center]2077[/align]
[align=center]Accepted[/align]
[align=center]375MS[/align]
[align=center] 23688K[/align]
[align=center]1991Byte[/align]
[align=center]G++[/align]
[align=center]2015-03-31 15:26:51.0[/align]
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
#include <algorithm>
#include <map>
#include <string>
#include <queue>

#define ll long long
int const N = 1000005;
int const M = 100005;
int const inf = 1000000000;
ll const mod = 1000000007;

using namespace std;

int n,a,b;
ll f
;        //n!
ll inv
;      //逆元
ll nf
;       //n!的逆元
int mi,ma;
ll ans;

void pre()
{
inv[0] = inv[1] = 1;
f[0] = f[1] = 1;
nf[0] = nf[1] = 1;
for (int i = 2; i < N; i++) {
inv[i] = ((mod - mod / i) * inv[mod % i]) % mod;
f[i] = (f[i - 1] * i)%mod;
nf[i] = (nf[i - 1] * inv[i])%mod;
}
}

ll calC(int n, int k)
{
return (f
* nf[k] % mod )* nf[n - k] % mod;
}

void ini()
{
if(b<a){
swap(a,b);
}
mi=a*n;
ma=b*n;
ans=0;
// printf("  mi=%d ma=%d\n",mi,ma);
}

void dfs(int sum)
{
//printf(" sum=%d\n",sum);
if(sum>ma){
return;
}
if(sum>=mi){
int fenzi=sum-mi;
int fenmu=b-a;
int y;
//printf("  sum=%d fenzi=%d mu=%d\n",sum,fenzi,fenmu);
if(fenzi%fenmu==0){
y=fenzi/fenmu;
//printf("  sum=%d fenzi=%d mu=%d y=%d\n",sum,fenzi,fenmu,y);
if(y>=0 && y<=n){
ans=(ans+calC(n,y))%mod;
}
}
}
dfs(sum*10+a);
dfs(sum*10+b);
}

void solve()
{
if(a==b){
int ssum=n*a;
while(ssum){
int yu=ssum%10;
if(yu!=a){
return;
}
ssum/=10;
}
ans++;
return;
}
dfs(0);
}

void out()
{
printf("%I64d\n",ans);
}

int main()
{
pre();
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
//scanf("%d",&T);
//for(int cnt=1;cnt<=T;cnt++)
//while(T--)
while(scanf("%d%d%d",&a,&b,&n)!=EOF)
{
ini();
solve();
out();
}
}


F

自动售货机

时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 60 测试通过 : 13

题目描述

教学楼有一台奇怪的自动售货机,它只售卖一种饮料,单价5元,并且只收5元、10元面值的货币,但是同学们都很喜欢喝。这个售货机里没有多余的找零,也就是说如果一个持有10元的同学第一个购买,则他不能获得5元找零,但是如果在他之前有一个持有5元的同学买了这种饮料,则他可以获得5元找零。

假设售货机的货源无限,每人只买一罐,现在有N个持有5元的同学和M个持有10元的同学想要购买,问一共有多少种排队方法可以让每个持有10元的同学都获得找零。(这里的排队方法以某一位置上人持的钱数来分,即只要同一位置上的同学所持钱的数目相同,就算同一种排队方法)

输入

多组测试数据

每组包含两个整数N,M(1<=M<=N<=1000),分别表示持有5元和10元的同学个数。

输出

输出一个整数,表示排队方法总数。由于结果可能很大,所以结果需要模1000000007。

样例输入

1 1
2 1
3 1

样例输出

1
2
3

题目来源

hjp

题解转自田神:
http://blog.csdn.net/tc_to_top/article/details/44745987
题目分析:这题其实很简单,开始想的太复杂了,开始当作卡特兰数 (Catalan数)做其实就是(C(n+m, m)%mod - C(n+m, m-1)%mod)%mod,数组递推组合数T了 ,用java写大数结果noj上mle,连乘组合数取余写跪了,下面说这题应该怎么做。

其实就是一个dp,dp[i][j]表示有i个人有5元,j个人有10元的排队方案数,我们可以发现:

dp[i][0] = 1,因为大家都只有5元,怎么排都是一种方案

i < j时dp[i][j] = 0,因为有5元的人比有10元的少,必然会出现找不开的情况,那么此时方案数就是0

不是以上两种情况时:dp[i][j]的方案数由dp[i - 1][j] + dp[i][j - 1]递推得到,考虑第m+n个人的状态

1.第m+n个人100,m+n-1里有m个50,n-1个100则dp[m][n-1]
2.第m+n个人50,m+n-1里有m-1个50,n个100则dp[m-1]

最后竟然忘了先预处理算一遍,- -

[align=center]njczy2010[/align]

[align=center]2078[/align]

[align=center]Accepted[/align]

[align=center]937MS[/align]

[align=center] 16020K[/align]

[align=center]887Byte[/align]

[align=center]G++[/align]

[align=center]2015-03-30 20:03:08.0[/align]

#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
#include <queue>
#include <algorithm>

#define ll long long
int const N = 1005;
int const M = 205;
int const inf = 1000000000;
ll const mod = 1000000007;

using namespace std;

ll dp[2*N]
;
int n,m;

void ini1()
{
memset(dp,0,sizeof(dp));
dp[1][0]=1;
int i,j;
for(i=2;i<=2000;i++){
for(j=0;j<=i/2;j++){
dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%mod;
}
}
}

void ini()
{

}

void solve()
{

}

void out()
{
printf("%I64d\n",dp[n+m][m]);
}

int main()
{
ini1();
//freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
//scanf("%d",&T);
//for(int cnt=1;cnt<=T;cnt++)
// while(T--)
while(scanf("%d%d",&n,&m)!=EOF)
{
ini();
solve();
out();
}
}


H:

KSS的金牌梦1
时间限制(普通/Java) : 3000 MS/ 9000 MS 运行内存限制 : 65536 KByte
总提交 : 56 测试通过 : 7

题目描述
KSS是nupt集训队里公认的最具有金牌实力的选手,熟练掌握多种金牌算法,但是由于队友水平太菜和自身情绪不稳定,一直没能拿到金牌。KSS为了圆梦,想为自己制定一个训练计划,那么问题来了:
ACM中有许多算法之间是有单方面依赖关系的,比如:想学会A,就必须先学B,由于KSS很聪明,所以它可以学完A再学B;当然也存在两种或多种算法相互交融的情况,比如:想学会A,就必须先学B,想学会B,就必须先学A,这种情况KSS就不知从何下手了。
现在给出KSS打算学习的一些算法之间的依赖关系,KSS将尽自己最大的努力去学习这些算法。再给出比赛会出现的算法,如果KSS能学会超过70%的比赛算法,他就能圆梦,否则,他只能含恨退役。

输入
多组测试用例。
第一行一个整数N(0<=N<=250000)表示有N对算法间存在依赖关系,保证涉及的算法总数不超过500
接下来N行每行有两个字符串(以空格分割),表示前一个算法依赖后一个算法,第N+1行有一个整数M(0<M<=1000)表示比赛会出现M个算法,接下来M行每行有一个字符串表示比赛出现的算法。(字符串保证不含空格)

输出
如果KSS可以圆梦,输出“Excelsior!”,否则,输出“KSS have a dream!”。(不用输出引号)

样例输入
4
Aho-Corasickautomaton KMP
Aho-Corasickautomaton trietree
Inclusion-ExclusionPrinciple Mobiusinversion
Mobiusinversion Inclusion-ExclusionPrinciple
5
KMP
trietree
Aho-Corasickautomaton
Splay
Suffixarray

样例输出
KSS have a dream!

提示
对于样例,KSS可以学会KMP、trietree、Aho-Corasickautomaton,但是并不能学会Inclusion-ExclusionPrinciple、Mobiusinversion,所以只能掌握60%的比赛算法

题目来源
hjp

题目分析转自田神:
http://blog.csdn.net/tc_to_top/article/details/44745987 题目分析:最伤心的一题。。。其实并不难可是全场就2人过还是在最后时候,所以并没有开它而是选择一直被数论坑着,赛后一下就补出来了。

这题就是裸的拓扑排序,出题人比较良心,没有卡map和cin的时间,不然字符串hash写就麻烦了,所以直接map存一下,建个图,拓扑排个序最后判断一下就行了,吐槽一下,我还是相信kss可以拿到金牌的

简单说下拓扑排序,就是用栈维护一个入读为0的点集,每次删点(出栈)然后修改图上各剩余点的入度,再将入度为0的点入栈,一直到栈为空,即不存在入度为0的点为止

[align=center]njczy2010[/align]

[align=center]2080[/align]

[align=center]Accepted[/align]

[align=center]1812MS[/align]

[align=center] 1496K[/align]

[align=center]2136Byte[/align]

[align=center]G++[/align]

[align=center]2015-03-31 14:44:13.0[/align]

#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
#include <algorithm>
#include <map>
#include <string>
#include <queue>

#define ll long long
int const N = 505;
int const M = 100005;
int const inf = 1000000000;
ll const mod = 1000000007;

using namespace std;

int n;
map<string,int>mp;
vector<int> bian
;
int r
;
int ok
;
int tot;
int sum;
int m;
char s1[M],s2[M];
int vis
;

void ini()
{
mp.clear();
memset(r,0,sizeof(r));
memset(ok,0,sizeof(ok));
memset(vis,0,sizeof(vis));
tot=0;
sum=0;
int i;
for(i=0;i<=500;i++){
bian[i].clear();
}
for(i=1;i<=n;i++){
scanf("%s%s",s1,s2);
if(mp.count(s1)==0){
tot++;mp[s1]=tot;
}
if(mp.count(s2)==0){
tot++;mp[s2]=tot;
}
int te1,te2;
te1=mp[s1];
te2=mp[s2];
r[te1]++;
bian[te2].push_back(te1);
}
}

void solve()
{
queue<int>que;
int i;
int te;
vector<int>::iterator it;
for(i=1;i<=tot;i++){
if(r[i]==0){
que.push(i);
vis[i]=1;
}
}
while(que.size()>=1){
te=que.front();
que.pop();
ok[te]=1;
for(it=bian[te].begin();it!=bian[te].end();it++){
int y=*it;
r[y]--;
if(r[y]==0 && vis[y]==0){
que.push(y);
vis[y]=-1;
}
}
}

scanf("%d",&m);
for(i=1;i<=m;i++){
scanf("%s",s1);
if(mp.count(s1)!=0){
te=mp[s1];
if(ok[te]==1){
sum++;
}
}
}
}

void out()
{
if(sum*10>=m*7){
printf("Excelsior!\n");
}
else{
printf("KSS have a dream!\n");
}
}

int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
//scanf("%d",&T);
//for(int cnt=1;cnt<=T;cnt++)
//while(T--)
while(scanf("%d",&n)!=EOF)
{
ini();
solve();
out();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐