9.6 模拟试题

 XXY  NOIP 模拟赛 1

题目

 

全排列

 

 

走楼梯

部落

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

stair

people

英文题目与子目录名

 

permutation

 

 

 

 

 

 

 

 

 

 

单个测试点时间限制

 

1秒

 

 

1

1

 

 

 

 

 

 

 

内存限制

 

128M

 

 

128M

128M

 

 

 

 

 

 

测试点数目

 

10

 

10

10

 

 

 

 

 

 

每个测试点分值

 

10

 

10

10

 

 

 

 

 

比较方式

 

全文比较(过滤行末空格及文末回车)

 

 

 

 

 

 

 

题目类型

 

传统

 

 

传统

传统

 

 

 

 

 

 

 

 

 

 

 

 

 

全排列

Description

个不同元素中任取 m(m≤n)个元素,按照一定的顺序排列起来,叫做从个不同元素中取出 m 个元素的一个排列。当 m=n 时所有的排列情况叫全排列。把全排列按字典序排序,现在 xxy 想问你第个全排列是什么,或给定的排列是第几个全排列

Input

第一行有两个整数 n 和 m 表示元素个数和询问个数接下来 m 行,

如果输入的字母为 P,接下来有一个整数 x,表示询问第 x 个全排列如果输入的字母为 Q,接下来有一个 n 元素的排列,表示询问这是第几个全排列

Output

对于询问,输出一个元素的排列。对于询问,输出一个整数 x

保证答案在 long long 范围内

Example input

5 2

P 3

Q

1 2 5 3 4

Output

1 2 4 3 5

5

Hint

对于 20%的数据,n<=5,m<=20对于另外 20%的数据,n<=10,m<=100,且无操作 P

对于另外 20%的数据,n<=10,m<=100,且无操作 Q 对于 100%的数据,n<=20,m<=1000

改编自:P3014 [USACO11FEB]牛线Cow Line

 

今天才知道解决这个问题可以用到康托展开,不,可以说是今天才知道有康托展开这个东西

(一篇很好的讲康托展开的博客:http://blog.csdn.net/acdreamers/article/details/7982067

康托展开表示的是当前排列在n个不同元素的全排列中的名次。比如213在这3个数所有排列中排第3。

那么,对于n个数的排列,康托展开为:

其中表示第i个元素在未出现的元素中排列第几。

举个简单易懂的例子:

对于排列4213来说,4在4213中排第3,注意从0开始,2在213中排第1,1在13中排第0,3在3中排第0,即:

 

,这样得到4213在所有排列中排第ans=20

 

对于这道题,我们就可以利用康拓展开

#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 21000
#define ll long long 
using namespace std;
char ch;
bool vis[N];
ll x,s[N],ans,ans1[N],n,m,t,sum,v[N],str[N];
ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
ll work1(ll x)
{
    memset(ans1,0,sizeof(ans1));
    for(int i=1;i<=n;i++)
     v[i]=i;
    for(int i=n;i>=1;i--)
    {
        t=x/s[i-1];
        x%=s[i-1];
        t++;
        sort(v+1,v+1+n);
        ans1[n-i+1]=v[t];
        v[t]=N;
    }
    for(int i=1;i<=n;i++)
     printf("%lld ",ans1[i]);
}
int work2()
{
    for(int i=1;i<=n;i++)
    {
        sum=0;
        for(int j=i+1;j<=n;j++)
         if(str[j]<str[i]) sum++;
        ans+=s[n-i]*sum;
    }
    return ans++;
}
int main()
{
    n=read(),m=read();s[0]=1;
    for(int i=1;i<=n;i++)
     s[i]=s[i-1]*i;
    while(m--)
    {
        cin>>ch;
        if(ch=='P')
        {
            x=read();
            x--;
            work1(x);
            printf("\n");
        }
        else
        {
            ans=0;
            for(int i=1;i<=n;i++) str[i]=read();
            work2();
            printf("%lld\n",ans);
        }
    }
}
康拓展开AC代码

 

走楼梯

Description

在你成功地解决了上一个问题之后,xxy 不禁有些气恼,于是她在楼梯上跳来跳去,想要你求出她跳的方案数。..

xxy 站在一个 n 阶楼梯下面,他每次可以往上跳一步或两步,往下跳一步到三步(由于地心引力跳得比较远),而且在往下跳的时候只能踩在往上跳时踩过的格子。

现在 xxy 在楼梯上乱跳,想问她跳到楼梯顶上最后又跳回楼梯下面的方案数 mod 2333333。

注意:xxy 只能一直向上跳,跳到楼梯最上面,然后再往下跳,跳回楼梯最底下。

Input

一个整数 n

Output

方案数 mod 2333333

Example

Input

5

Output

42

Hint

对于 10%的数据,n<=5对于 30%的数据,n<=10

对于 100%的数据,n<=1000000

改编自:U3357 C2-走楼梯

 

dp(详细思路见:上一篇博文 T2  http://www.cnblogs.com/z360/p/7496500.html

 

向下走可以看成向上走

 

f[i]表示第一次向上走到i,第二次向上也走到i的方案数

如果第二次向上走1步到i,这1步第一次有1种走法

如果第二次向上走2步到i,这2步第一次有2种走法

如果第二次向上走3步到i,这3步第一次有3种走法

如果第二次向上走4步到i,这4步第一次有5种走法

所以状态转移方程:f[i]=f[i-1]+f[i-2]*2+f[i-3]*3+f[i-4]*5

 

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 11000000
#define mod 2333333
using namespace std;
long long n,m,x,y,z,f[5],dp[N];
long long read()
{
    long long x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int main()
{
    n=read();f[0]=1;dp[0]=1;
    for(int i=1;i<=4;i++)
     for(int j=1;j<=2;j++)
      if(i-j>=0) f[i]+=f[i-j];
      else break;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=4;j++)
      if(i-j>=0) dp[i]=(dp[i]+dp[i-j]*f[j])%mod;
      else break;
    printf("%lld\n",dp[n]);
    return 0;       
}
洛谷AC代码 m=4时
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 11000000
#define mod 2333333
using namespace std;
long long n,m,x,y,z,f[4],dp[N];
long long read()
{
    long long x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int main()
{
    n=read();f[0]=1;dp[0]=1;
    for(int i=1;i<=3;i++)
     for(int j=1;j<=2;j++)
      if(i-j>=0) f[i]+=f[i-j];
      else break;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=3;j++)
      if(i-j>=0) dp[i]=(dp[i]+dp[i-j]*f[j])%mod;
      else break;
    printf("%I64d\n",dp[n]);
    return 0;       
}
改编题代码 m=3时

 

部落

Description

很久很久以前,有一个岛,岛上有个人,人们经常爆发战争。经 xxy 研究发现,如果两个人住的越近,那么他们之间爆发战争的可能性就越大。Xxy 还发现,如果将某些人按居住地划分为个部落,那么部落内部的人们为了部落的强大便不会发生战争。这就意味着,划分为个部落后,不同的部落的人住的越近,爆发战争的可能性就越大。设为任意两个不同部落的人之间的欧几里得距离,xxy 想知道在爆发战争可能性最小的情况下,最小的是多少。

注:欧几里得距离即平面上两个点的距离

Input

第一行两个整数 n,k。

接下来行,每行两个整数 x,y 表示第个人的坐标

Outpu

最小的 d,保留位小数

Example

 

 

people.in

people.out

 

 

 

4

2

1.00

0

0

 

0

1

 

1

1

 

1

0

 

 

 

 

Hint

30%的数据,k<=n<=10

50%的数据,k<=n<=100

100%的数据,k<=n<=1000,0<=x<=100000,0<=y<=10000

 

这真是道大水题,可是蒟蒻在考试的时候没想出来、、、

使这几个部落划分成m个部落的最小边不就是他最小生成树中要添加的第n-m条边吗????

要将这n各部落全部连接起来需要n-1条边,我们要将它划分成m个部落,也就是说我们要有m个部落不能被连接起来,我们要先将距离小的两个部落先连起来,这就恰好是最小生成树的思想,所以要想到最小生成树也不难。我们将这m个部落连起来恰好是用m-1条边,也就是说我们将除了那m个部落连起来,恰好是要用n-1-(m-1)也就是n-m条边,然后这m部落间的最小的距离就是n-m+1条边的长度了

#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 1100
using namespace std;
double z,ans;
int n,m,s,x,y,fx,fy,sum,xx[N],yy[N],fa[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct Edge
{
    int x,y;
    double z;
}edge[N*N];
int cmp(Edge a,Edge b)
{
    return a.z<b.z;
}
int find(int x)
{
    if(fa[x]==x) return x;
    fa[x]=find(fa[x]);
    return fa[x];
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++) xx[i]=read(),yy[i]=read();
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      if(i!=j) 
      {
           s++;
           z=sqrt((double)pow(xx[i]-xx[j],2)+(double)pow(yy[i]-yy[j],2));
           edge[s].x=i;
           edge[s].y=j;
           edge[s].z=z;
      }
    for(int i=1;i<=n;i++) fa[i]=i;
    sort(edge+1,edge+1+s,cmp);
    for(int i=1;i<=s;i++)
    {
        x=edge[i].x,y=edge[i].y;
        fx=find(x),fy=find(y);
        if(fa[fx]==fy) continue;
        fa[fx]=fy;
        sum++;
        if(sum==n-m+1) {ans=edge[i].z; break;}
    }
    printf("%.2lf",ans);
    return 0;
 } 
最小生成树、、

转载于:https://www.cnblogs.com/z360/p/7496680.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值