校内赛 2017.8.27 【卢卡斯定理】 【DFS+Hash】【线段树】

来源:互联网 发布:excel2007软件 编辑:程序博客网 时间:2024/06/05 22:59

Description

crf 是一个天才。
他出生的第一秒,就已经学完了整本《组合数学》。
他觉得作为高中生的你,应该比才出生一秒的他差不到哪去,于是他决定向你问一个问题。
crf 想知道,从n 个物品中选出m 个的方案数,由于他很讨厌很长的答案,所以他想知道模p
后的答案。

Input

输入的第一行为三个整数n; m; p。

Output

输出共一行,为一个数字,代表答案。

Sample

Sample Input Sample Output
5 3 7 3
Sample Input Sample Output
125 77 71 63

Hint

对于30% 的数据,满足1  m  n  10; 1  p  10。
对于70% 的数据,满足1  m  n  100000。
对于100% 的数据,满足1  m  n  1000000000; 1  p  100000。
对于100% 的数据,满足p 为质数。

思路

卢卡斯定理即可

代码

#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>#include<cmath>#include<vector>using namespace std;const int N=1e9;long long n,m,mod;long long qmul(long long a,long long b){    long long rt;    for (rt=0;b;b>>=1,a=(a+a)%mod)    if (b&1) {rt+=a;rt%=mod;}    return rt;}long long qpow(long long a,long long b){    long long rt;    for (rt=1;b;b>>=1,a=qmul(a,a)%mod)    if (b&1) rt=qmul(rt,a)%mod;    return rt;}long long C(long long n,long long m){    long long ans=1;    for (long long i=1;i<=m;i++)    {        long long a=(n+i-m)%mod;        int b=i%mod;        ans=ans*(a*qpow(b,mod-2)%mod)%mod;    }    return ans;}long long lucas(long long n,long long m){    if (m==n) return 1;    return (C(n%mod,m%mod)*lucas(n/mod,m/mod))%mod;}int main(){    freopen("first.in","r",stdin);    freopen("first.out","w",stdout);    scanf("%I64d%I64d%I64d",&n,&m,&mod);    if (m>n) {printf("0");return 0;}    printf("%I64d",lucas(n,m)%mod);    return 0;}

Description

crf 是一个天才。
他出生的第二秒,往窗外望了一眼,看到了窗外的树林。
他发现这片树林非常有趣,因为它只有一排树,从左到右依次排列。
天才的crf 马上就把真实的树的结构抽象成为了图论中的树(即任意两个顶点有且仅有一条路
径可以互相到达的图)。
crf 不仅头脑天才,而且他的身体素质也堪称天才,这也包括了他可以在0.01 秒内用AWP 爆
掉队友的头的动态视力。在这一秒内他发现非常多的树其实稍微变一下方向就是长得一样的。
他想知道,在这片树林中有多少棵树和他现在脑中yy 的这棵树是本质相同的。
两棵树本质相同的定义是,对于树A,存在一种顶点编号的排列,使得树A 的顶点编号重排
后,树A 的每一条边的方向和两个顶点都和树B 对应相同。
注意,虽然图论中大多数对树的定义都是指无根树,即每条树边都是无向边的树,但在这个问
题中的树是现实中的树的抽象,所以我们认为这些树都是有根树。
同时为了方便起见,我们假设每棵树具有相同的结点数。

Input

第一行为三个整数n; m; q,表示这排树林树的数量、每棵树的结点数和询问的数量。
接下来n 部分,每部分有m��1 行,表示一棵树的m��1 条边,每行有两个整数x; y,表示从
x 到y 有一条边。
接下来q 部分,每部分有m��1 行,表示询问中的一棵树的m��1 条边,每行有两个整数x; y,
表示从x 到y 有一条边。

Output

输出有q 行,每行一个整数,表示该次询问有多少棵树和crf 想象的那棵树本质相同。

思路

经典的树同构,构造括号序列,在每个结点处把子树的括号序列进行排序并且加上一对括号即可。
一般的做法是将左括号和右括号分别赋一个质数的值,用hash值进行比较和记录,怕卡hash的话可以使用双hash来保险,本题因为复杂度的原因标程使用了直接用括号序列进行比较。

代码

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>#include<vector>using namespace std;const int N=10000+5;int n,m,q,num,ans=0,tot=0,cnt;int head[N],get[N][4],headd[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 v,next;};edge ed[N],now[N];struct data{    int in,out;    int flag;};data tree[N],tr[N];void build(int u,int v){    ed[++num].v=v;    ed[num].next=head[u];    head[u]=num;    tree[u].out++;    tree[v].in++;}void add(int u,int v){    now[++cnt].v=v;    now[cnt].next=headd[u];    headd[u]=cnt;    tr[u].out++;    tr[v].in++;}int main(){    freopen("second.in","r",stdin);    freopen("second.out","w",stdout);    n=read();m=read();q=read();    if (m==1) {printf("%d\n",n);return 0;}    memset(head,-1,sizeof(head));    memset(tree,0,sizeof(tree));    memset(tr,0,sizeof(tr));    for (int k=1;k<=n;k++)    {        int now=tot*(m-1);        for (int i=1;i<m;i++)        {            int x,y;            x=read();y=read();            get[i+now][1]=x;get[i+now][2]=y;        }        tot++;    }//100000    tot=0;    while(q--)    {        memset(tr,0,sizeof(tr));        memset(headd,-1,sizeof(headd));        for (int i=1;i<m;i++)        {            int x,y;            x=read();y=read();            add(x,y);        }        for (int k=1;k<=n;k++)        {            memset(head,-1,sizeof(head));            memset(tree,0,sizeof(tree));            for (int i=1;i<=n;i++)            tr[i].flag=0;            int u,v;            for (int i=1;i<m;i++)            {                u=get[i+tot*(m-1)][1],v=get[i+tot*(m-1)][2];                build(u,v);            }            for (int i=1;i<=m;i++)            for (int j=1;j<=m;j++)            if(tr[i].in==tree[j].in&&tr[i].out==tree[j].out&&tree[j].flag==0)            {tree[j].flag=1;tr[i].flag=1;}            for (int i=1;i<=n;i++)            if (tree[i].flag!=1) {ans--;break;}            ans++;tot++;        }        printf("%d\n",ans);        ans=0;tot=0;    }    return 0;}

Description

crf 是一个天才。
他出生的第三秒,偶然看到了一张简谱,可能是医院的工作人员闲的没事的时候创作的。
当时的他并不能理解简谱和音乐的关系(但这并不妨碍他今后成为世界著名小提琴演奏家),
他只是单纯的看着这张写满1-7 的纸,觉得非常缺乏美感。他认为有序的数列才是最美的。
但毕竟他才出生第三秒,他的mogic 也只是初步显现,他可以改变一段连续的音符的顺序,但
他的mogic 不足以一次改掉整张简谱。
这是crf 这辈子第一次犯蠢,也是唯一一次犯蠢。他尝试着将一段一段的音符调成升序,但他
并没有意识到就算这样调了也不能让整个序列变得有序。
crf 的mogic 可以选出一段连续的音符,将它们重新排为升序之后再放回,他尝试了一些这样
的操作之后发现并没有将序列变得有序就放弃了。
后来的程序员为了纪念伟大的crf,将他唯一犯蠢的这段经历重新变成了一道算法题。
虽然这道题大部分题面都是对crf 的赞美,但可能你们不敢兴趣所以就直接上题吧。
给定一个长度为n 的只有1-7 的数字的序列,有q 次操作,每次操作是将一段连续的数字拿出
来,排成升序或者降序后放回。求最终序列。

Input

输入的第一行为两个整数n; q,表示序列的长度和操作的数量。
接下来一行有n 个整数,表示crf 的数字序列。
接下来q 行,每行有三个整数l; r; op,l; r 含义如题目中所示,op 为0 表示升序,为1 表示降
序。

Output

输出共1 行,包含n 个整数,表示最终的序列,中间用空格隔开。
Sample Input Sample Output
5 2
3 1 2 5 4
1 3 0
2 5 1
1 5 4 3 2

Hint

对于30% 的数据,保证1  n; q  1000。

思路

线段树,因为只有7种数字,用线段树维护每一段的7种数字分别有多少,每次操作即为一次查询和七次覆盖

代码

#include<cstdio>#include<iostream>#include<cstring>const int maxn = 100010;struct Node{    int cnt[7];    int size;    int cover;    Node()    {        memset(cnt, 0, sizeof(cnt));        cover = -1;    }    inline void set(int cover)    {        this->cover = cover;        for (int i = 0; i < 7; i ++)            if (i == cover)                cnt[i] = size;            else                cnt[i] = 0;    }    inline friend Node operator + (const Node &a, const Node &b)    {        Node c;        for (int i = 0; i < 7; i ++)            c.cnt[i] = a.cnt[i] + b.cnt[i];        return c;    }}t[maxn << 2];int a[maxn];inline void update(int rt){    t[rt].size = t[rt << 1].size + t[rt << 1 | 1].size;    for (int i = 0; i < 7; i ++)        t[rt].cnt[i] = t[rt << 1].cnt[i] + t[rt << 1 | 1].cnt[i];}inline void pushdown(int rt){    if (t[rt].cover != -1)    {        t[rt << 1].set(t[rt].cover);        t[rt << 1 | 1].set(t[rt].cover);        t[rt].cover = -1;    }}inline void build(int l, int r, int rt){    if (l == r)    {        t[rt].size = 1;        t[rt].cnt[a[l]] = 1;        return;    }    int mid = (l + r) >> 1;    build(l, mid, rt << 1);    build(mid + 1, r, rt << 1 | 1);    update(rt);}inline Node query(int l, int r, int rt, int L, int R){    if (l >= L && r <= R)        return t[rt];    pushdown(rt);    int mid = (l + r) >> 1;    Node answer;    if (L <= mid)        answer = answer + query(l, mid, rt << 1, L, R);    if (R > mid)        answer = answer + query(mid + 1, r, rt << 1 | 1, L, R);    return answer;}inline void change(int l, int r, int rt, int L, int R, int cover){    if (l >= L && r <= R)    {        t[rt].set(cover);        return;    }    pushdown(rt);    int mid = (l + r) >> 1;    if (L <= mid)        change(l, mid, rt << 1, L, R, cover);    if (R > mid)        change(mid + 1, r, rt << 1 | 1, L, R, cover);    update(rt);}int main(){    freopen("third.in","r",stdin);    freopen("third.out","w",stdout);    int n, q;    scanf("%d%d", &n, &q);    for (int i = 1; i <= n; i ++)    {        scanf("%d", &a[i]);        a[i] --;    }    build(1, n, 1);    for (int i = 1; i <= q; i ++)    {        int l, r, op;        scanf("%d%d%d", &l, &r, &op);        Node answer = query(1, n, 1, l, r);        int nowl = l;        if (op == 0)        {            for (int j = 0; j < 7; j ++)                if (answer.cnt[j])                {                    change(1, n, 1, nowl, nowl + answer.cnt[j] - 1, j);                    nowl += answer.cnt[j];                }        }        else        {            for (int j = 6; j >= 0; j --)                if (answer.cnt[j])                {                    change(1, n, 1, nowl, nowl + answer.cnt[j] - 1, j);                    nowl += answer.cnt[j];                }        }    }    for (int i = 1; i <= n; i ++)    {        Node answer = query(1, n, 1, i, i);        int x = -1;        for (int j = 0; j < 7; j ++)            if (answer.cnt[j])                x = j + 1;        printf("%d%c", x, i == n ? '\n' : ' ');    }    return 0;}