国庆清北刷题冲刺班 Day3 上午

a

【问题描述】
你是能看到第一题的 friends 呢。
——hja
怎么快速记单词呢?也许把单词分类再记单词是个不错的选择。何大爷给
出了一种分单词的方法,何大爷认为两个单词是同一类的当这两个单词的各个
字母的个数是一样的,如 dog 和 god。现在何大爷给了你?个单词,问这里总共
有多少类单词。
【输入格式】
第一行一个整数?代表单词的个数。
接下来?行每行一个单词。
【输出格式】
一行一个整数代表答案。
【样例输入】
3
AABAC
CBAAA
AAABB
【样例输出】
2
【数据范围与规定】
70%的数据,1 ≤ ? ≤ 100。
对于100%的数据,1 ≤ ? ≤ 10000,所有单词由大写字母组成。

set + string 水水

#include<iostream>
#include<cstring>
#include<string>
#include<set>
#include<cstdio>
#include<algorithm>
using namespace std;
set<string> Ans;
int main(int argc,char *argv[]){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n; scanf("%d", &n);
    string s;
    for(int i=1; i<=n; ++i){
        cin >> s;
        sort (s.begin(), s.end());
        Ans.insert(s);
    }
    printf("%d\n",Ans.size());
    fclose(stdin); fclose(stdout);
    return 0;
}

b

【问题描述】
你是能看到第二题的 friends 呢。
——laekov
长度为?的铁丝,你可以将其分成若干段,并把每段都折成一个三角形。你
还需要保证三角形的边长都是正整数并且三角形两两相似,问有多少种不同的
分法。
【输入格式】
一行一个整数?。
【输出格式】
一行一个整数代表答案对10 9 + 7取模之后的值。
【样例输入 1
6
【样例输出 1】
2
【样例输入 2】
9
【样例输出 2】
6
【样例解释 2】
(1,1,1),(2,2,2);(2,2,2),(1,1,1)算两种方案。
【数据范围与规定】
3。
60%的数据,1 ≤ ? ≤ 1000。
对于100%的数据,1 ≤ ? ≤ 10 6 。

// 题目原型  HDU 4466 容斥原理   自己搜题解吧
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;

#define Mod 1000000007
#define MAXN 1000010
int dp[MAXN],bin[MAXN],n;
inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}

void init(){
    dp[3]=1;
    for(int i=4; i<MAXN; ++i) {
        dp[i] = dp[i - 1] + (i-1)/2 - i/3 + (i%3 ? 0 : 1);
        if(!(i&1)) dp[i] -= i/4;
        if(dp[i] >= Mod) dp[i] -= Mod;
    }
    bin[1] = 1;
    for(int i=2; i<MAXN; ++i){
        bin[i] = (bin[i - 1] << 1) % Mod;
        for(int j=2; i * j<MAXN; ++j){
            dp[i * j] -= dp[i];
            if(dp[i * j] < 0) dp[i *j] += Mod;
        }
    }
    return ;
}

int main(int argc,char *argv[]){
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    scanf("%d", &n);
    init();
    long long Ans = 0;
    for(int i=1; i * i <= n; ++i){
        if(n % i) continue;
        Ans = (Ans + 1LL * dp[i] * bin[n / i]) % Mod;
        if(i * i != n) Ans = (Ans + 1LL * dp[n / i] * bin[i]) % Mod;
    }
    printf("%I64d\n",Ans);
    fclose(stdin);fclose(stdout);
    return 0;
}

c

【问题描述】
你是能看到第三题的 friends 呢。
——aoao
在小学的时候,我们都学过正视图和左视图。现在何大爷用一些小方块摆了
一个图形,并给出了你这个图形的左视图和正视图。现在何大爷希望知道,在给
定正视图和左视图的情况下,原来的立体图形有多少种可能的情况?
【输入格式】
第一行两个整数?,?,代表在左视图和正视图中分别有多少列。
第二?个整数,代表在左视图中从左至右每一列的高度。
第三行?个整数,代表在正视图中从左至有每一列的高度。
【输出格式】
一行一个整数代表答案对10 9 + 9取模之后的值。
【样例输入 1】
2 2
1 1
1 1
【样例输出 1】
7
【样例输入 2】
4 5
5 2 4 1
5 2 4 0 1
【样例输出 2】
429287
【数据规模与约定】
21 ≤ ?,? ≤ 5,每列的最大高度不超过5。
40%的数据,? + ? ≤ 18。
对于100%的数据,1 ≤ ?,? ≤ 50,每列最大高度不超过10000。

#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 10001
#define Mod 1000000009
int n,m,C[52][52],a[MAXN],b[MAXN];

void pre(int k) {
    for(int i=0; i<=k; ++i) C[i][0] = 1;
    for(int i=1; i<=k; ++i)
        for(int j=1; j<=i; ++j)
            C[i][j] = (C[i-1][j-1] + C[i-1][j]) % Mod;
}
int pow(long long a,int b){
    long long res = 1;
    while(b){
        if(b & 1) res = (1LL * res * a) % Mod;
        a = a * a % Mod;b >>= 1;
    }
    return res;
}

int calc(int n,int m,int nn,int mm,int h){
    int res=0, tmp;
    for(int i=0; i<=nn; ++i)
        for(int j=0; j<=mm; ++j){
            tmp = 1LL * pow(h,n * m - (n - i) * (m - j)) * pow(h + 1,(n - i)*(m - j) - (n - nn) * (m - mm)) % Mod *
                C[nn][i] % Mod * C[mm][j] % Mod;
            if((i + j)&1) res =( (res-tmp) %Mod + Mod ) %Mod;
            else res += tmp,res %= Mod;
        }
    return res;
}

inline void read(int &x){
    x=0; int f=1;register char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}

int main(int argc,char *argv[]){
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    read(n),read(m);
    for(int x,i=1; i<=n; ++i) read(x), ++a[x];
    for(int x,i=1; i<=m; ++i) read(x), ++b[x];
    pre(max(n,m));
    long long Ans = 1 ;
    int nown=0,nowm=0;
    for(int i=10000; i>=0; --i)
        if(a[i] || b[i]){
            nown += a[i], nowm += b[i];
            Ans = 1LL * Ans * calc(nown,nowm,a[i],b[i],i) % Mod;
        }
    printf("%I64d\n", Ans);
    fclose(stdin);fclose(stdout);
    return 0;
}

分析:这题竟然是个容斥原理
解决本题的关键:行交换。列交换对答案不影响
将左视图按从下往上递减,正视图从左往右递减排列
那整张图的高度从左下到右上呈阶梯状递减
这样所有高度相同的呈现倒‘L’形,如下图所示蓝色部分
如果我们按高度递减的顺序依次计算每个倒‘L’形的方案数,那么这些倒‘L’形相对独立
答案就是所有倒‘L’形答案的乘积
这里写图片描述
如何计算单个倒‘L’形的答案?——容斥原理
假设上图为已经按高度排好序的图
设当前正在处理高度为h的倒‘L’形
令nn表示当前有nn行的左视图高度为h,mm表示当前有mm列的主视图高度为h
n表示当前有n行的左视图高度>=h,m表示当前有m列的主视图高度>=
定义性质pk表示 在这nn行mm列中,有k行/列不满足看到的高度为h
那根据容斥原理,
不具有任何一个性质p的方案和=
全集-Σ|pi|+Σ|pi∩pj|-Σ|pi∩pj∩pk|+…+(-1)^m*|p1∩p2∩…∩pm|
也就是所有方案-所有1行/列不满足条件的方案+所有2行/列不满足条件的方案-……
如何求有k行/列不满足条件的方案数?
设现在要求在倒‘L’形中,有i行j列不满足条件的方案数A,i+j=k
那么A分为两部分
① i行j列不能满足条件的部分:
当前高度为h,不能满足条件,每一个各自可以填[0,h-1],每个格子有h种方案
所以此时方案数=h^ (n*m-(n-i)*(m-j))
② 倒‘L’形中其他位置可以任意填的部分
当前高度为h,任意填就是可以填[0,h],每个各自有h+1种方案
所以此时的方案数=(h+1)^((n-i)(m-j)-(n-nn)(m-mm))
这是选定i行j列,所以还要乘上在nn行中选i行,在mm列中选j列的方案
终上所述,每个倒‘L’形 的答案为 (-1)^(i+j)* C(nn,i)* C(mm,j)* h^ (n*m-(n-i)(m-j)) (h+1)^((n-i)(m-j)-(n-nn)(m-mm))

声明: 分析来自cnblogs TRTTG

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值