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; }
