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

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

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

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

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

 

posted @ 2019-07-05 17:11  ACforever  阅读(1849)  评论(0编辑  收藏  举报