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;
}
来源:https://www.cnblogs.com/hyfer/p/11139510.html
