校内赛 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;}
- 校内赛 2017.8.27 【卢卡斯定理】 【DFS+Hash】【线段树】
- 17.8.27 校内赛 解题报告【卢卡斯定理】【dfs+hash】【线段树】
- 线段树+hash+codeforces213E
- 线段树 + 字符串Hash
- hdu4605 线段树+dfs
- 线段树+dfs HDU
- (线段树+dfs序)
- DFS序+线段树
- 【校内互侧】Sunshine’s city (lct+线段树)
- hdu 3973(hash+线段树)
- Codeforces452F Permutation -- 线段树 + Hash
- 校内赛
- HDU 1556(线段树+dfs)
- POJ3162 线段树+DFS
- HDU5692 线段树+dfs序
- 【线段树+dfs序】J
- 线段树2.0+dfs序
- hdu5238(中国剩余定理+线段树)
- CMA
- Effective C++总结
- 解决:According to TLD or attribute directive in tag file, attribute value does not accept any express。
- Linux数据库编程Sqlite
- 几种编程题高难度常用算法总结
- 校内赛 2017.8.27 【卢卡斯定理】 【DFS+Hash】【线段树】
- 列表、表格与媒体元素
- 两位数值单词的乘法实现
- springmvc下的省市县三级联动
- LeetCode 72. Edit Distance
- minicom 使用方法
- linux 查看端口是否被占用
- C#基础-024 猜数字游戏
- 客户端缓存和服务器缓存处理