长乐培训Day8

T1 远征

题目

【题目描述】

寒枫将军将要带领他的部队去圣雪山消灭那里的冰龙。部队分成了若干个小队,属于同一个小队的人兵种相同。

寒枫将军有着杰出的指挥能力,在战斗的时候,寒枫将军能够让所有相同兵种的人互相配合,使t个相同兵种的人发挥出t2的战斗力;

寒枫将军还能让不同兵种的人互相配合,使整个部队的战斗力是所有兵种战斗力的和。

例如,部队中有3个小队,分别是5个人的步兵小队,3个人的步兵小队,3个人的骑兵小队。那么步兵战斗力为64,骑兵战斗力为9,部队总战斗力为73。

寒枫将军需要知道他的部队的战斗力是多少。

【输入格式】

第一行一个整数n,表示小队数。接下来n行,第i行有两个整数ai、bi,表示这个小队有ai个人,兵种为bi

【输出格式】

一行一个整数,部队的战斗力。

【输入样例】

3

5 1

3 1

3 2

【输出样例】

73

【数据规模】

10%的数据,n=1

30%的数据,n≤1000

另有20%的数据,ai=1

另有30%的数据,bi≤1000

100%的数据,1≤n≤100000,1≤ai≤10000,1≤bi≤1,000,000,000

解析

T1依旧水,蒟蒻仍AC。

将a与b用结构体存储,再按b从小到大排序,如果i的b等于i-1的b,那就是相同兵种,令t累加上a,

如果不相等,那就是不同兵种,统计一下答案并把t清零,最后不要忘了再统计一次答案。

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
int read()
{
    int num=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        num=(num<<1)+(num<<3)+ch-'0';
        ch=getchar();
    }
    return num*w;
}
struct rec{
    int a,b;
}ra[100100];
int n;
long long ans,t;
bool cmp(rec x,rec y)
{
    return x.b<y.b;
}
int main()
{
    //freopen("expedition.in","r",stdin);
    //freopen("expedition.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++) ra[i].a=read(),ra[i].b=read();
    sort(ra+1,ra+n+1,cmp);
    t=ra[1].a;
    for(int i=2;i<=n;i++)
    {
        if(ra[i].b==ra[i-1].b) t+=ra[i].a;
        else
        {
            ans+=t*t;
            t=ra[i].a;
        }
    }
    ans+=t*t;
    cout<<ans;
    return 0;
    //fclose(stdin);
    //fclose(stdout);
}
View Code

 

 

 

 

 

T2 密码

题目

【题目描述】

假发通过了不懈的努力,得到了将军家门锁的密码(一串小写英文字母)。但是假发被十四和猩猩他们盯上了,所以假发需要把密码传递出去。

因为假发不想十四他们发现几松门前贴的小纸条就是将军家的密码,所以他加密了密码(新八:听起来有点诡异)。加密方法如下:随机地,在密码中任意位置插入随机长度的小写字符串。

不过,假发相信银桑和他那么多年小学同学,一定能猜中密码是什么的(新八:银桑什么时候成攮夷志士了!!!)。

可是,写完了小纸条之后,假发觉得有点长,就想截去头和尾各一段(可以为空),让剩下的中间那一段依然包含真~密码。想着想着,假发就想知道有多少种可行方案。

结果在沉迷于稿纸之际,假发被投进了狱门岛(新八:……)。于是,就由你计算了。 

【输入格式】

两行非空字符串,纯小写英文字母,第一行是加密后的密码,第二行是原密码。

第一行长度不超过300000,第二行不超过200。

【输出格式】

一行,有多少种方案。注意:不剪也是一种方案。

【输入样例】

abcabcabc

cba

【输出样例】

9

【数据规模】

30%的数据满足第一行长度不超过1000。

100%的数据满足第一行长度不超过300000,方案总数不超过10^18。

第二行长度小于213

解析

感谢机房大佬们的教学!

我们设f[i][j](初值为-1)表示加密后密码的第i位与原密码的第j位匹配时,加密后密码与原密码匹配的第0位数在加密后密码的位上的左边共有多少位(从第0位开始),

很绕对不对(本蒟蒻也看不懂自己在写什么),举个例子,abcabcabc与cba这个样例,

f[2][0]=2(加密后密码第2位与原密码第0位都是c,匹配,而在原密码第0位c对应的加密后密码第2位的前面共有2位),还是不懂?再多看几个例子,

f[3][1]=2(加密后密码第3位是a,而原密码第1位为b,不匹配,所以还是2),

f[4][1]=2(加密后密码第4位与原密码第1位都是b,匹配),

f[6][2]=2(加密后密码第6位与原密码第2位都是a,匹配,而在原密码第0位c对应的加密后密码的第2位的前面共有2位)。如果还不懂的话多看几遍、多试几遍。

然后状态转移即可,最后答案是所有f[i][原密码长度-1]+1的和(i从加密后密码长度-1枚举到原密码长度-1)。

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
int f[300100][220];
string s1,s2;
long long ans;
int main()
{
    //freopen("substring.in","r",stdin);
    //freopen("substring.out","w",stdout);
    memset(f,-1,sizeof(f));
    cin>>s1>>s2;
    for(int i=0;i<s1.size();i++)
        for(int j=0;j<s2.size();j++)
        {
            if(j==0)
            {
                if(s1[i]==s2[j]) f[i][0]=i;
                if(s1[i]!=s2[j]&&i>=1) f[i][0]=f[i-1][0];
            }
            else
            {
                if(s1[i]==s2[j]&&i>=1) f[i][j]=f[i-1][j-1];
                if(s1[i]!=s2[j]&&i>=1) f[i][j]=f[i-1][j];
            }
        }
    for(int i=s1.size()-1;i>=s2.size()-1;i--) ans+=f[i][s2.size()-1]+1;
    cout<<ans;
    return 0;
    //fclose(stdin);
    //fclose(stdout);
}
View Code

 

 

 

 

 

T3 独立集

题目

【题目描述】

有一天,一个名叫顺旺基的程序员从石头里诞生了。又有一天,他学会了冒泡排序和独立集。在一个图里,独立集就是一个点集,满足任意两个点之间没有边。

于是他就想把这两个东西结合在一起。众所周知,独立集是需要一个图的。那么顺旺基同学创造了一个算法,从冒泡排序中产生一个无向图。

这个算法不标准的伪代码如下:

Pascal版本

C/C++版本

procedure bubblesortgraph(n, a[]) :

/*输入:点数n,1到n的全排列a。

输出:一个点数为n的无向图G。*/

创建一个有n个点,0条边的无向图G。

repeat

   swapped = false

   for i 从 1 到 n-1 :

       if a[i] > a[i + 1] :

          在G中连接点a[i]和点a[i + 1]

          交换a[i]和a[i + 1]

          swapped = true

until not swapped

输出图G。

//结束。

void bubblesortgraph(n,a[])

  //输入:点数n,1到n的全排列a

  //输出:一个点数为n的无向图G

{  创建一个有n个点,0条边的无向图G。

   do{  swapped=false

        for i 从1 到n-1

          if(a[i]>a[i+1])

           {  在G中连接点a[i]和点a[i+1]

              交换a[i]和a[i+1]

              swapped =true

          }

     }while(swapped);

   输出图G。

}

//结束。

那么我们要算出这个无向图G最大独立集的大小。但是事情不止于此。顺旺基同学有时候心情会不爽,这个时候他就会要求你再回答多一个问题:

最大独立集可能不是唯一的,但有些点是一定要选的,问哪些点一定会在最大独立集里。今天恰好他不爽,被他问到的同学就求助于你了。

【输入格式】

输入包含两行,第一行为N,第二行为1到N的一个全排列。

【输出格式】

输出包含两行,第一行输出最大独立集的大小,第二行从小到大输出一定在最大独立集的点的编号。

【输入样例】

3

3 1 2

【输出样例】

2

2 3

【数据规模】

30%的数据满足 N<=16

60%的数据满足 N<=1,000

100%的数据满足 N<=100,000

解析

这题有点坑,刚看的时候想都没想,直接打了模拟,结果GG,后来想想才发现是最长上升子序列。

仔细看冒泡排序的代码,不难得出,只有两数为逆序对时才有边,反之没边,即上升子序列,而第一问显然就是最长上升子序列;

而第二问就可以转化为必定会在最长上升子序列的元素有哪些,我们可以正着求一遍最长上升子序列,再反向求一遍,

两个最长上升子序列都有的元素即为答案。

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
int read()
{
    int num=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        num=(num<<1)+(num<<3)+ch-'0';
        ch=getchar();
    }
    return num*w;
}
const int N=100100;
int n,a[N],k,maxn,f1[N],f2[N],d[N],b[N];
int main()
{
    //freopen("bubble.in","r",stdin);
    //freopen("bubble.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++)
    {
        k=lower_bound(d,d+maxn+1,a[i])-d;
        maxn=max(maxn,k);
        f1[i]=k;
        d[k]=a[i];
    }
    d[0]=0xcfcfcfcf,maxn=0;
    for(int i=n;i>=1;i--)
    {
        k=lower_bound(d,d+maxn+1,-a[i])-d;
        maxn=max(maxn,k);
        f2[i]=k;
        d[k]=-a[i];
    }
    cout<<maxn<<endl;
    for(int i=1;i<=n;i++)
        if(f1[i]+f2[i]-1==maxn) b[f1[i]]++;
    for(int i=1;i<=n;i++)
        if(f1[i]+f2[i]-1==maxn&&b[f1[i]]==1) cout<<i<<" ";
    return 0;
    //fclose(stdin);
    //fclose(stdout);
}
View Code

 

 

 

 

 

T4 选修课

题目

【题目描述】

温州中学开放了许多选修课,每节选修课都属于一种种类。精力旺盛的黄小龙同学想要尽可能多的参加选修课,但是他只能选择M种种类的课程。

当然,对于他所选的种类,他会去上所有该种类的课。现在他想知道他最多能上几节选修课,以及上最多选修课的方案数。

两种方案被认为不同当且仅当一种方案中存在另一种方案所没有的选修课。

【输入格式】

第一行一个只由小写字母组成,长度为N的字符串。表示有N节选修课,以及每节选修课的种类。

【输出格式】

输出一行,两个用空格隔开的正整数,分别表示最多选修课数量和方案数。

【输入样例】

abcde

1

【输出样例】

1 5

【数据规模】

对于30%的数据,M==1;

对于另外30%的数据,每种种类的选修课最多只有一节;

对于100%的数据1<=M<=26、1<=N<=100000;

解析

惊!某蒟蒻AC了T4!

今天的T4居然挺简单,有点不可思议。

因为字母总共只有26个,所以我们可以开一个a数组存储每个字母出现的次数,

再用贪心思想,把a数组从大到小排序,取前m个数累加就是第一问;

第二问得分两种情况,如果m≥所有字母种类,直接输出1即可;

如果m<所有字母种类的话,其实就是在求组合数,举个例子,

aaaabbbbcccdddeeffz(这些是字母出现次数,别问我为什么这么规则,反正只要记录出现次数就可以了(其实是懒)),

很显然,a4次,b4次,c3次,d3次,e2次,f2次,z1次。

如果m为1,在a和b两个中选一个即可,方案数为2;

如果m为2,很显然,取a和b这两个出现次数最多的即可,方案数为1,;

如果m为3呢?很显然,a和b这两个字母肯定必选,那么剩下的一个肯定得选c或d,方案数为2,是不是和m为1时类似?

如果m为4,选a,b,c,d,方案数为1;

如果m为5,a,b,c,d,这四个数肯定还是要选,那么剩下的一个就是e或f,方案数为2;

剩下的以此类推,本蒟蒻便不分别说了。

仔细分析上面的例子,不难发现,在m增加的过程中,我们每次都会选在m较小时所选的数以及剩下的数中最大的,

如果当前有多个最大的,那么就是在求最大的数的种类数中选择x个数(x为m剩下的未选择的次数),这不就是组合数吗?

思路想通了,代码也就不难了,具体在操作上面的步骤时,用递归就可以了。

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
int read()
{
    int num=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        num=(num<<1)+(num<<3)+ch-'0';
        ch=getchar();
    }
    return num*w;
}
int m,a[27],temp,ans,sum,f[27][27];
string s;
bool cmp(int x,int y)
{
    return x>y;
}
void work(int last,int x)
{
    int num=0;
    for(int i=last+1;i<=26;i++)
    {
        if(a[i]==0) break;
        if(a[i]==a[last+1]) num++;
        else break;
    }
    if(num>=x)//从num个数中选x个数 
    {
        for(int i=1;i<=num;i++)
            for(int j=0;j<=i;j++) f[i][j]=f[i-1][j]+f[i-1][j-1];
        cout<<f[num][x];
    }
    else work(last+num,x-num);
}
int main()
{
    //freopen("course.in","r",stdin);
    //freopen("course.out","w",stdout);
    cin>>s;
    m=read();
    for(int i=0;i<s.size();i++) a[s[i]-'a'+1]++;
    sort(a+1,a+27,cmp);
    f[0][0]=1;
    for(int i=1;i<=26;i++)
    {
        if(a[i]==0) break;
        else sum++;
        if(temp<m)
        {
            ans+=a[i];
            temp++;
        }
    }
    cout<<ans<<" ";
    if(m>=sum) cout<<"1";
    else work(0,m);
    return 0;
    //fclose(stdin);
    //fclose(stdout);
}
View Code

转载于:https://www.cnblogs.com/I-Love-You-520/p/11266196.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值