2017 Chinese Multi-University Training, BeihangU Contest
http://codeforces.com/gym/102253
A
用m个二进制位可以表示10^k,给定m,问k最大是多少
乘一个lg2即可
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 300050; int n; int main(){ int T,i=0; double m; while(scanf("%lf",&m)!=EOF){ i++; m = m*log(2)/log(10); printf("Case #%d: %d\n",i,(int)floor(m)); } return 0; }
K
一个1-n的序列,每次选一个最小的放进暂存区,当暂存区有n-1个数时,下一次取数后把这n-1个数再放回序列,问第k次取的是多少
推出一个规律:第一次一定是全部取一遍,之后每次都有一个取不到进行下一轮循环,这个取不到的数是最大的和次大的交替出现
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 300050; ll n,k; int main(){ int i = 0; while(scanf("%I64d%I64d",&n,&k)!=EOF){ printf("Case #%d: ",++i); if(k<=n){ printf("%I64d\n",k); }else{ k-=2; ll r=k/(n-1); ll tmp=1ll+(k%(n-1)); if(tmp==n-1)tmp+=((r+1)%2); printf("%I64d\n",tmp); } } return 0; }
B
将一些字母串转换为26进制的数字,每个字母对应一个数字,要求这些字母转换成数后和最大,并且不能包含前导零
分开每个字母计算贡献,然后排序。因为数字较大,需要自己动计算进位、比较大小
因为不能包含前导零,如果根据排序结果,分配到0的字母对应的是前导零,就需要找到一个不是零的字母,然后不断往后换。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 100150; const ll mod = 1e9+7; int n; char s[maxn]; ll val[50]; bool isZero[50]; struct dat{ ll val; ll ss[maxn]; int len; bool zo; bool operator < (const dat& b) const{ for(int i = maxn-1;i>=1;i--){ if(ss[i]!=b.ss[i])return ss[i]>b.ss[i]; } return ss[1]>b.ss[1]; } }dats[26]; int main(){ ios::sync_with_stdio(false); int T = 0; while(cin>>n){ memset(val,0,sizeof(val)); memset(isZero,false,sizeof(isZero)); for(int i = 0;i <= 25;i++){ memset(dats[i].ss,0,sizeof(dats[i].ss)); dats[i].len=0; dats[i].val=0; dats[i].zo=false; } for(int j = 1;j <= n;j++) { cin>>(s+1); int l = strlen(s + 1); if(l>1)dats[s[1]-'a'].zo=true; for (int i = l; i >= 1; i--) { dats[s[i]-'a'].ss[l-i+1]++; int t = l-i+1; while(dats[s[i]-'a'].ss[t]==26){ dats[s[i]-'a'].ss[t] = 0; t++; dats[s[i]-'a'].ss[t]++; } } } fo(i,0,25){ for(int j = maxn-5;j>=1;j--){ dats[i].val *= 26ll; dats[i].val += dats[i].ss[j]; dats[i].val %= mod; } } sort(dats,dats+26); int sheep=250; if(dats[25].zo){ for(int i = 24;i >= 0;i--){ if(!dats[i].zo){ sheep=i; break; } } for(int i = sheep;i < 25;i++){ swap(dats[i],dats[i+1]); } } ll ans = 0; fo(i,0,25){ ans = (ans + (ll)(25ll-(ll)i)*dats[i].val) % mod; } cout<<"Case #"<<++T<<": "<<ans<<endl; } return 0; }
F
求一个n的排列A到m的排列B的映射,要求f(i)=b(f(a(i))),求方案数。
由f(i)可以推知f(a(i)),由于是排列到排列的映射,i->a(i)->a(a(i))...最终一定会回到它自身,从而形成一个环。
把这个环求出来,由上面的式子可以推知,不断地令i=b(i),走i对应的环的长度,最终一定要等于i,1-n的每一个位置的映射值等于满足这个条件的b(i)的数量。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 100050; const ll mod = 1e9+7; int n,m; int a[maxn]; int b[maxn]; int br[maxn][30]; int amt[maxn]; bool vis[maxn]; vector<int> hasApp; bool dfs(int x,int fa,int deep){ vis[x]=true; if(a[x]==fa){ amt[deep]++; }else{ dfs(a[x],fa,deep+1); } } bool canCir(int x,int y){ int k = 0; int oy = y; while(x){ if(x&1)y = br[y][k]; k++; x >>= 1; } //cout<<x<<" "<<y<<" "<<oy<<endl; return oy == y; } int main(){ int T = 0; while(scanf("%d%d",&n,&m)!=EOF){ fo(i,0,n-1)scanf("%d",&a[i]); fo(i,0,m-1)scanf("%d",&b[i]); memset(amt,0,sizeof(amt)); memset(vis,0,sizeof(vis)); hasApp.clear(); fo(i,0,m-1){ br[b[i]][0] = i; } fo(k,1,22){ fo(i,0,m-1){ br[i][k] = br[br[i][k-1]][k-1]; } } fo(i,0,n-1){ if(!vis[i]) dfs(i,i,1); } ll ans = 1; fo(i,1,n){ if(amt[i]) hasApp.push_back(i); } int sz = hasApp.size(); for(int i = 0;i < sz;i++){ ll ansi = 0; fo(j,0,m-1){ if(canCir(hasApp[i],j)){ ansi++; } } while(amt[hasApp[i]]){ amt[hasApp[i]]--; ans = (ans*ansi) % mod; } } printf("Case #%d: ",++T); printf("%I64d\n",ans); } return 0; }
L
求1-n的排列的个数,其中第i个要求是(li,ri)的所有子区间的最小值都是pi,其他包含i的区间的最小值都不是p。
首先,如果当前考虑的区间是(l,r),则一定有一个约束是包含整个区间的,我们把这个区间对应的位置i找出来,然后递归他的左边区间和右边区间。
此时,左区间和右区间是不相关的,没有第二个跨越这两个区间的约束,两个区间的答案可以用组合数计算。
如何快速找到包含整个区间的约束?将区间排序即可。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 1000050; const ll mod = 1e9+7; inline ll read(){ ll 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 seg{ int l; int r; int p; friend bool operator < (seg a,seg b){ if(a.l!=b.l) return a.l < b.l; return a.r > b.r; } }s[maxn]; int n; int nowpos; ll fac[maxn]; ll inv[maxn]; ll C(ll n,ll m){ return fac[n]*inv[m]%mod*inv[n-m]%mod; } ll dfs(int l,int r){ if(l != s[nowpos].l || r != s[nowpos].r) return 0; int mid = s[nowpos].p; nowpos++; ll ansl=1,ansr=1; if(l<mid) ansl = dfs(l,mid-1); if(r>mid) ansr = dfs(mid+1,r); return ansl*ansr%mod*C(r-l,mid-l)%mod; } int main(){ int T = 0; fac[0]=fac[1]=1; fo(i,2,maxn-1){ fac[i] = (fac[i-1]*i)%mod; } inv[0]=inv[1] = 1; fo(i,2,maxn-1){ inv[i]=(mod-(mod/i))*inv[mod%i]%mod; } fo(i,2,maxn-1){ inv[i] = (inv[i]*inv[i-1])%mod; } while(scanf("%d",&n)!=EOF){ fo(i,1,n){ s[i].p=i; s[i].l=read(); } fo(i,1,n){ s[i].r=read(); } sort(s+1,s+1+n); nowpos=1; ll ans=dfs(1,n); printf("Case #%d: ",++T); printf("%I64d\n",ans); } return 0; }