T1是一个大组合数取模,用的是线性求逆元,貌似忘了,想了很久,
艰难A掉
T2最大生成树有一个性质,任意两点间的边权最小的边一定是连接两点
间所有路径中最大的(否则可以替换,使最大生成树变大)。
这个题目,其实是求最大生成森林的过程,考虑Kruskal 过程,每合并
两个集合,实际上选的这条边是连接两个集合最大的限重。也就是货
物重量低于这个值,就可以通过这条边在两个集合之间运输,否则这
两个集合就一定至少需要两个仓库。
所以整个kruskal 过程,实际上每次是在求,重量低于多少时可以减少
一个仓库。于是一开始有n 个仓库,把减少仓库的关键点记下来,对
于每个询问二分即可,或者使用lower_bound。
T3
很GG
没听懂
首先不好处理的一点是,一个合法的单词有多种拆分方法。
可以认为从最后一个合法的位置拆分。
但是需要处理。
cool,o 这种情况。
然后用一个trie 树进行计数,详见代码,a 数组是长度为i 的前缀,后
面接j 字母不可能还是前缀的数量。b 数组是长度为i 的前缀,最后一
个字母是j 并且不是词典中的单词的数量。c 数组是长度为i 的后缀,
第一个字母是j 的数量。
合并这三个数组即可。
T1 立方体
#include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> using namespace std; const int N = 1e6 + 10; #define Lgj 1000000007 #define LL long long LL jc[N], Inv[N], inv[N]; int n, k, my; inline int read() { int x = 0; char c = getchar(); while(c < '0' || c > '9') c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c -'0', c = getchar(); return x; } void before() { jc[0] = jc[1] = 1; inv[0] = inv[1] = 1; Inv[0] = Inv[1] = 1; for(int i = 2; i <= 1000005; ++ i) { jc[i] = ( jc[i - 1] % Lgj * i % Lgj ) % Lgj; inv[i] = (1LL * (- (Lgj / i) * inv[Lgj % i])) % Lgj; if(inv[i] < 0) inv[i] += Lgj; Inv[i] = ( Inv[i - 1] % Lgj * inv[i] % Lgj ) % Lgj; if(Inv[i] < 0) Inv[i] += Lgj; } } LL C(int n,int m) { LL ret; ret = ( (jc[n] % Lgj * Inv[m] % Lgj) % Lgj * Inv[n - m] % Lgj ) % Lgj; return ret; } int main(int argc, char *argv[]) //这个东西听zjr说貌似能加速,试试呗 { freopen("cube.in", "r", stdin); freopen("cube.out", "w", stdout); before(); n = read(); k = read(); for(int i = 1; i <= n; i ++ ) my = read(); LL answer = C(n, k); printf("%I64d ",answer); return 0; }
T2 仓库
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cassert> using namespace std; typedef long long LL; int n,m,q; struct N{ int x,y,z; }edge[100005]; int fa[100005]; bool cmp(N a,N b){ return a.z>b.z; } int get(int x){ return fa[x]==x?x:fa[x]=get(fa[x]); } int p[100005],cc,w; int main(){ freopen("warehouse.in","r",stdin); freopen("warehouse.out","w",stdout); scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=m;i++){ scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z); } for(int i=1;i<=n;i++) fa[i]=i; sort(edge+1,edge+m+1,cmp); for(int i=1;i<=m;i++){ if(get(edge[i].x)!=get(edge[i].y)){ fa[get(edge[i].x)] = edge[i].y; p[++cc] = edge[i].z; } } assert(cc == n-1); reverse(p+1,p+cc+1); while(q--){ scanf("%d",&w); int pos = lower_bound(p,p+cc+1,w)-p; // printf("# %d ",pos); printf("%d ",pos); } return 0; }
T3 单词
#include <cstdio> #include <cstring> #include <iostream> #include <cassert> #include <algorithm> using namespace std; typedef long long LL; LL mod = 1e9+7; int n,q; char s[55]; LL cnt[105]; int a[55][26]; int b[55][26]; int c[55][26]; struct Trie{ int trie[500050][26]; int flag[500050]; int tot; void insert(char * s){ int id = 0; for(int i=0;s[i];i++){ if(trie[id][s[i]-'a']) id = trie[id][s[i]-'a']; else id = trie[id][s[i]-'a']=++tot; } flag[id] ++; assert(flag[id] == 1); } void dfs(int x,int l){ for(int i=0;i<26;i++){ if(trie[x][i] == 0 && x){ a[l][i] ++; } if(trie[x][i] && x && !flag[trie[x][i]]){ b[l+1][i] ++; } if(trie[x][i]) dfs(trie[x][i],l+1); } } void solve1(){ dfs(0,0); } void dfs2(int x,int l){ for(int i=0;i<26;i++){ if(trie[x][i]){ c[l+1][i]++; dfs2(trie[x][i],l+1); } } } void solve2(){ dfs2(0,0); } }t1,t2; int main(){ freopen("word.in","r",stdin); freopen("word.out","w",stdout); scanf("%d%d",&n,&q); for(int i=1;i<=n;i++){ scanf("%s",s); int L = strlen(s); t1.insert(s); reverse(s,s+L); t2.insert(s); cnt[L]++; } t1.solve1(); t2.solve2(); for(int l=1;l<=50;l++){//b for(int i=0;i<26;i++){ assert(c[1][i] == 0 || c[1][i] == 1); if(c[1][i]){ cnt[l]+=b[l][i]; } } } for(int l1 = 1;l1 <=50;l1++){ for(int l2 = 1;l2 <=50;l2++){ for(int i=0;i<26;i++){ cnt[l1+l2] += a[l1][i]*1LL * c[l2][i]; } } } int L; while(q--){ scanf("%d",&L); printf("%d ",(int)(cnt[L]%mod)); } return 0; }