2017 清北济南考前刷题Day 5 morning

期望得分:100+100+0=200

实际得分:

坐标的每一位不是0就是1,所以答案就是

C(n,k)

#include<cstdio>
#include<iostream>

using namespace std;

const int mod=1e9+7;

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); }
}

int Pow(int a,int b)
{
    int res=1;
    for(;b;a=1LL*a*a%mod,b>>=1)
        if(b&1) res=1LL*res*a%mod;
    return res;
}

int main()
{
        freopen("cube.in","r",stdin);
    freopen("cube.out","w",stdout);
    int n,k;
    read(n); read(k);
    int ans=1;
    for(int i=1;i<=k;i++)
        ans=1LL*ans*Pow(i,mod-2)%mod*(n-i+1)%mod;
    cout<<ans;
}

货物一定是走在最大生成树上

如果按限重从大到小加边,那么一个联通块只需要有一个仓库

并查集维护即可

考场代码写的有点儿乱

#include<algorithm>
#include<iostream>
#include<cstdio>

using namespace std;

#define N 100001

int n,m;

int fa[N];
bool cal[N];

int ans[N],tmp[N];

bool vis[N];

struct node
{
    int u,v,lim;
    bool use;
}e[N];

struct query
{
    int id,w;
}a[N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); }
}

bool cmp(node p,node q) { return p.lim>q.lim; }

bool cmp2(query p,query q) { return p.w>q.w; }

bool cmp3(node p,node q)
{
    if(p.use!=q.use) return p.use>q.use;
    return p.lim>q.lim;
}

int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); }

void MST()
{
    for(int i=1;i<=n;i++) fa[i]=i;
    int tot=0,i=0,u,v;
    while(tot!=n-1 && i<m)
    {
        i++;
        u=find(e[i].u); v=find(e[i].v);
        if(u==v) continue;
        tot++; e[i].use=true;
        fa[u]=v;
    }
}

int main()
{
    freopen("warehouse.in","r",stdin);
    freopen("warehouse.out","w",stdout);
    int q;
    read(n); read(m); read(q);
    for(int i=1;i<=m;i++) read(e[i].u),read(e[i].v),read(e[i].lim);
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=q;i++) read(a[i].w),a[i].id=i;
    sort(a+1,a+q+1,cmp2);
    MST();
    sort(e+1,e+m+1,cmp3);
    for(int i=1;i<=n;i++) fa[i]=i;
    int ne=1,nq=1; int sum=n;
    while(ne<=m)
    {
        if(!e[ne].use) break;
        while(ne<=m && e[ne].lim>=a[nq].w)
        {
            if(find(e[ne].u)==find(e[ne].v))
            {
                ne++;
                continue;
            }
            fa[find(e[ne].u)]=find(e[ne].v);
            sum--;
            ne++;
        }
        ans[a[nq].id]=sum;
        nq++;
    }
    for(int i=nq;i<=q;i++) ans[a[i].id]=sum;
    for(int i=1;i<=q;i++) cout<<ans[i]<<‘\n‘;
}

答案=原词典单词数+前缀后缀拼接单词数

统计前缀和后缀 拼接的单词:

一个基本错误思路:

如果有s1个长为l1 的前缀,有s2个长为l2的后缀,那么 他们对长为l1+l2 的单词的贡献为 s1*s2

错因:有可能会重复计数

例:

有单词 abc   bcd

那么合法单词abcd 会计算3次:

a+bcd   ab+cd   abc+d

所以对于每个合法的单词,强行规定一个分离位置,来保证一个单词只会被计算一次

规定的分离位置:

若单词长为i的前缀是词典中单词的前缀,长为i+1的前缀不是词典中单词的前缀

那么i就是单词的分离位置

即这个单词是由前面长为i的前缀 和 i后面的后缀 拼接而成

在这个不会算重的思路的基础上

设f[i][j] 表示 原词典单词中 长度为i的前缀,再加1个字母j就不是前缀的 前缀数量

g[i][j] 表示 原词典单词中 长为i的后缀,第1个字母是j的 后缀数量

这两个数组在 正序和倒序 trie树上dfs即可解决

那么长为 i+k 的 前缀后缀 拼接单词数量 = Σ f[i][j]*g[k][j]   j∈[0,26)

但思路仍然还有一个bug:

长为len 的 单词 的所有前缀 都是词典中单词的前缀

例:

词典中单词 cool   o

那么 co 是一个长为2的合法单词

但是co按上述方法找不到分离位置

对于这种单词,我们强制规定 最后两个字母之间为分离位置

注意:这种单词还要满足 不是原词典中的单词,后缀非空 两个条件

所以 设t[i][j] 表示 长为i的前缀,最后一个字母是j 且 不是词典中单词的数量

注意这里不要把长为1的前缀统计进去

如果 g[1][j] 那么 长为i的合法单词 就可以累计 t[i][j]

#include<cstdio>
#include<cstring>
#include<algorithm>

#define N 10001
#define L 51

using namespace std;

const int mod=1e9+7;

char s[L];

int ans[101];

int f[L][26],g[L][26],t[L][26];

struct TRIE
{
    int trie[N*L][26],id;

    int endd[N*L];

    void insert(int len)
    {
        int now=0;
        for(int i=1;i<=len;i++)
        {
            if(!trie[now][s[i]-‘a‘]) trie[now][s[i]-‘a‘]=++id;
            now=trie[now][s[i]-‘a‘];
        }
        endd[now]++;
    }

    void dfs(int x,int dep)
    {
        for(int i=0;i<26;++i)
        {
            if(x && !trie[x][i]) f[dep][i]++;
            if(x && trie[x][i] && !endd[trie[x][i]]) t[dep+1][i]++;
            if(trie[x][i]) dfs(trie[x][i],dep+1);
        }
    }

    void dfs2(int x,int dep)
    {
        for(int i=0;i<26;++i)
            if(trie[x][i]) g[dep+1][i]++,dfs2(trie[x][i],dep+1);
    }

}t1,t2;

int main()
{
    freopen("word.in","r",stdin);
    freopen("word.out","w",stdout);
    int n,q,len;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        len=strlen(s+1);
        t1.insert(len);
        reverse(s+1,s+len+1);
        t2.insert(len);
        ans[len]++;
    }
    t1.dfs(0,0);
    t2.dfs2(0,0);
    for(int i=1;i<=50;++i)
        for(int j=0;j<26;++j)
            if(g[1][j]) ans[i]=(ans[i]+t[i][j])%mod;
    for(int i=1;i<=50;++i)
        for(int k=1;k<=50;++k)
            for(int j=0;j<26;++j)
                ans[i+k]=(1LL*ans[i+k]+f[i][j]*g[k][j]%mod)%mod;
    int x;
    while(q--)
    {
        scanf("%d",&x);
        printf("%d\n",ans[x]);
    }
}

时间: 2024-08-15 14:01:53

2017 清北济南考前刷题Day 5 morning的相关文章

2017 清北济南考前刷题Day 5 afternoon

期望得分:100+100+30=230 实际得分:0+0+0=30 T1 直接模拟 #include<cstdio> #include<iostream> using namespace std; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } #define N 1002

2017 清北济南考前刷题Day 1 morning

期望得分:100+100+50=250 实际得分:100+60+50=210 T2 二分 估错上界.估错复杂度 T1 立方数(cubic) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK定义了一个数叫“立方数”,若一个数可以被写作是一个正整数的3次方,则这个数就是立方数,例如1,8,27就是最小的3个立方数. 现在给定一个数P,LYK想要知道这个数是不是立方数. 当然你有可能随机输出一些莫名其妙的东西来骗分,因此LYK有T次询问~ 输入格式(cub

2017 清北济南考前刷题Day 3 morning

实际得分:100+0+0=100 T1 右上角是必败态,然后推下去 发现同行全是必胜态或全是必败态,不同行必胜必败交叉 列同行 所以n,m 只要有一个是偶数,先手必胜 #include<cstdio> using namespace std; int main() { freopen("star.in","r",stdin); freopen("star.out","w",stdout); int n,m; whi

2017 清北济南考前刷题Day 2 morning

期望得分:100+30+60=190 实际得分:100+30+30=160 T1 最优方案跳的高度一定是单调的 所以先按高度排序 dp[i][j] 跳了i次跳到j 枚举从哪儿跳到j转移即可 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 51 struct node { int c,h; }e[N]

清北考前刷题day7早安

清北考前刷题da7下午好

三向城 /* 原图一定是一棵完全二叉树. 根节点是x,左节点是x*2,右节点是x*2+1 转化为二进制往左右走就很明显了. */ #include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; int T,x,y,k,ans,pos; inline int read() { int x=0,f=1;char c=getchar(); while

清北考前刷题day6下午好

/* 贪心 负数一定不取 枚举最高位是1 且答案取为0的 位置, 更新答案. */ #include<iostream> #include<cstdio> #include<cstring> #define ll long long #define N 100010 using namespace std; int n; ll a[N],ans,sum[N]; char s[N]; ll read() { ll x=0,f=1;char c=getchar(); whi

2017清北精英班整理内容掌握考试题

精英班考试题 2017.2.10 题目名 工资 藏妹子之处 银河之星 源文件 money.cpp/c/pas excel.pas/cpp galaxy.cpp/c/pas 输入文件 money.in excel.in galaxy.in 输出文件 money.out excel.out galaxy.out 时间限制 1000MS 1000MS 1000MS 内存限制 256MB 128MB 256MB 测试点 10 10 10 测试点分值 10 10 10 第一题 工资 链接 第二题  藏妹子

2017清北学堂集训笔记——图论

我们进入一个新的模块——图论! emmmmm这个专题更出来可能有点慢别介意,原因是要划的图和要给代码加的注释比较多,更重要的就是...这几个晚上我在追剧!!我们的少年时代超级超级超级好看,剧情很燃啊!!咳咳,好吧下面回归正题. 一.图的存储: 1.邻接矩阵: 假设有n个节点,建立一个n×n的矩阵,第i号节点能到达第j号节点就将[i][j]标记为1(有权值标记为权值), 样例如下图: 1 /*无向图,无权值*/ 2 int a[MAXN][MAXN];//邻接矩阵 3 int x,y;//两座城市