2017 Chinese Multi-University Training, BeihangU Contest
A Add More Zero
题目链接
题意:用m个二进制位可以表示10^k,给定m,问k最大是多少
思路:换底计算
#include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; using ll = long long; int main() { int n; int ca = 0; while (~scanf("%d", &n)) { int ans; ans = (int)ceil(1.0*n*log10(2.0))-1; printf("Case #%d: %d\n", ++ca,ans); } }
B Balala Power!
题目链接
题意:将一些字母串转换为26进制的数字,每个字母对应一个数字,要求这些字母转换成数后和最大,并且不能包含前导零
思路:大数比较、模拟、进制转换
#include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> #include<set> #include<queue> #include<algorithm> #include<string> #include<cmath> #include<stack> #include<functional> #include<bitset> typedef long long ll; using namespace std; const int mod = 1e9 + 7; char a[100005]; int val[30][100005]; struct node{ int no, score, lead; }rankk[30], t; bool cmp(node x,node y){ return x.score > y.score; } int main(){ int cas=1,n, maxl; ll sum; while(scanf("%d",&n)!=EOF){ for(int i=0;i<26;i++){ rankk[i].no = i; rankk[i].score = 0; rankk[i].lead = 0; } maxl = 0; memset(val,0,sizeof val); for(int i=1;i<=n;i++){ scanf("%s",a); int len=strlen(a); maxl = max(maxl,len); for(int j=0;j<len;j++){ val[a[j]-'a'][len - j - 1]++; } rankk[a[0] - 'a'].lead++; } for(int i=0;i<26;i++){ for(int j=0;j<maxl-1;j++){ val[i][j+1] += val[i][j] / 26; val[i][j] %= 26; } } for(int i=0;i<25;i++){ for(int j=i + 1;j<26;j++){ for(int k=maxl-1;k >= 0;k--){ if(val[i][k] > val[j][k]){ rankk[i].score++; break; } else if(val[i][k] < val[j][k]){ rankk[j].score++; break; } } } } sum = 0; sort(rankk,rankk+26,cmp); if(rankk[25].lead){ for(int i = 24;i >= 0;i--){ if(!rankk[i].lead){ t = rankk[i]; for(int j = i;j < 25;j++){ rankk[j] = rankk[j + 1]; } rankk[25] = t; break; } } } for(int i=0;i<26;i++){ ll res = 1; for(int j=0;j<maxl;j++){ sum = (sum + res * (25 - i) % mod * val[rankk[i].no][j]) % mod; res = res * 26 % mod; //cout << sum << endl; } //cout << sum << endl; } printf("Case #%d: %I64d\n",cas++,sum); } }
F Function
题目链接
题意:求一个n的排列A到m的排列B的映射,要求f(i)=b(f(a(i))),求方案数。
思路:由f(i)可以推知f(a(i)),由于是排列到排列的映射,i->a(i)->a(a(i))...最终一定会回到它自身,从而形成一个环。即a的某个长度为x环可以和b中长度为x因子的环构成函数。
Tarjin求强联通,计算即可。
#include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; int a[maxn], b[maxn]; int vis[maxn]; vector<int>v, G[maxn]; int c[maxn]; int tuan = 0; void dfs(int x) { //cout << "dfs: " << x << endl; tuan++; if (vis[b[x]] == -1) { vis[b[x]] = 1; dfs(b[x]); } } int dfn[maxn], low[maxn], ins[maxn], stk[maxn]; int num = 0, top = 0, cnt = 0; void tarjan(int x) { dfn[x] = low[x] = ++num; stk[++top] = x; ins[x] = 1; for (int y : G[x]) { if (!dfn[y]) { tarjan(y); low[x] = min(low[x], low[y]); } else if (ins[y]) { low[x] = min(low[x], dfn[y]); } } if (dfn[x] == low[x]) { cnt++; int yy; do { yy = stk[top--]; ins[yy] = 0; c[yy] = cnt; } while (x != yy); } } unordered_map <int, int>book; int n, m; void init() { num = 0, top = 0, cnt = 0; for (int i = 0; i <= n; i++) { c[i] = 0; G[i].clear(); dfn[i] = 0; } book.clear(); v.clear(); } int main() { int cas=1; while (~scanf("%d%d", &n, &m)) { init(); for (int i = 0; i < n; i++) { scanf("%d", &a[i]); } for (int i = 0; i < m; i++) { scanf("%d", &b[i]); } memset(vis, -1, sizeof vis); for (int i = 0; i < m; i++) { //cout << "YYY" << endl; if (vis[i]==-1) { vis[i] = 1; tuan = 0; dfs(i); v.push_back(tuan); } } for (int i : v) { // cout << "array b: " << i << endl; book[i]++; } for (int i = 0; i < n; i++) { G[a[i]].push_back(i); } for (int i = 0; i < n; i++) { if (!dfn[i])tarjan(i); } unordered_map<int, int>mp; int ma = 0; for (int x = 0; x < n; x++) { for (int y : G[x]) { if (c[x] == c[y]) { mp[c[x]]++; } } } long long ans = 1; for (auto p : mp) { long long res=0; for (int i = 1; i <= sqrt(p.second); i++) { if (p.second%i == 0) { if(book[i]) res += book[i]*i; if (i == p.second / i)continue; if(book[p.second/i]) res+= book[p.second/i] * (p.second/i); } } ans*=res; // cout << " YYY : " << p.first << " " << p.second << endl; } printf("Case #%d: ",cas++); printf("%lld\n", ans); } } /* 3 2 1 0 2 0 1 3 4 2 0 1 0 2 3 1 6 4 2 0 1 0 5 4 0 2 3 1 */
K KazaQ's Socks
题目链接
题意:一个1-n的序列,每次选一个最小的放进暂存区,当暂存区有n-1个数时,下一次取数后把这n-1个数再放回序列,问第k次取的是多少
思路:模样例,找规律
#include <bits/stdc++.h> typedef long long ll; using namespace std; ll n,m; void solve(){ if(m<n){ printf("%lld\n",m); }else{ m-=n; ll a=m/(n-1); ll b=m%(n-1); if(b==0) a--; if(a%2==0) if(b==0) printf("%lld\n",n-1); else printf("%lld\n",b); else{ if(b==0) printf("%lld\n",n); else printf("%lld\n",b); } } } int main(){ int cas=1; while(scanf("%lld%lld",&n,&m)!=EOF){ printf("Case #%d: ",cas++); solve(); } }
L Limited Permutation
题目链接
题意:对于每个下标给定一个区间[li,ri]表示对于此区间中最小值为pi,且pi为[li,ri]的最小值,li和ri都不能再扩张。求满足的排列种类数
思路:对于1来说,其区间一定为[1,n]。我们根据下标将其分为左右两个区间,组合数+递归计算。但直接递归会爆栈,通过排序区间,在区间上进行dfs。注意不满足情况的输出值为0。
#include <bits/stdc++.h> #define maxn 1000005 typedef long long ll; using namespace std; const int MOD=1000000007; struct node{ int l,r; int index; }q[maxn]; int n; ll fac[maxn]; ll inv[maxn]; ll invfac[maxn]; void init() { fac[1]=1;inv[1]=1;invfac[1]=1; fac[0]=1;inv[0]=1;invfac[0]=1; for(int i=2;i<=1000000;i++) { fac[i]=fac[i-1]*i%MOD; inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD; invfac[i]=invfac[i-1]*inv[i]%MOD; } } ll C(int n,int m) { return fac[n]*invfac[m]%MOD*invfac[n-m]%MOD; } bool cmp(node a,node b){ if(a.l==b.l) return a.r>b.r; return a.l<b.l; } int cnt=1; ll dfs(int l,int r){ // cout<<l<<' '<<r<<' '<<q[cnt].l<<' '<<q[cnt].r<<endl; if(l!=q[cnt].l||r!=q[cnt].r) return 0; int mid=q[cnt].index; cnt++; if(l==r) return 1; ll ans=1; ans=ans*C(r-l,mid-l)%MOD; if(l<mid) ans=ans*dfs(l,mid-1)%MOD; if(r>mid) ans=ans*dfs(mid+1,r)%MOD; return ans%MOD; } void input(){ for(int i=1;i<=n;i++){ // q[i].l=i; scanf("%d",&q[i].l); } for(int i=1;i<=n;i++){ // q[i].r=n; scanf("%d",&q[i].r); q[i].index=i; } } void solve(){ sort(q+1,q+1+n,cmp); cnt=1; ll ans=dfs(1,n)%MOD; printf("%lld\n",ans); } int main(){ init(); int cas=1; while(scanf("%d",&n)!=EOF){ input(); printf("Case #%d: ",cas++); solve(); } }