2017 Multi-University Training Contest - Team 1

1001
题意:
给定m,求最大的k满足10^k<2^m

分析:
显然kmax=log10(2^m-1), 但m太大,直接计算2^m-1不现实,log10(2^m)计算很方便,
Log10 (2^m)=m*log10(2),我们发现只有当x(x=2^m)末位为0时,log10(2^m)!=log10(2^m-1),但x作为2的幂,末位必不为0,所以log10(2^m)=log10(2^m-1),那么ans=m*log10(2)。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<iostream>
#include<algorithm>
#define LL unsigned long long
#define debug puts("Infinity is awesome!")
#define mm(a, b) memset(a, b, sizeof(a))
#define rep(a, b, c) for(int a=b;a<c;a++)
#define rerep(a, b, c) for(int a=b;a>=c;a--)
#define reset(a, b) memset(a, b, sizeof(a))
using namespace std;
const int Inf=1e9;
const int MOD=1e9+7;
int m;
int main(){
    int cas=0;

    while(~scanf("%d", &m)){
        double ans=m*log(2)/log(10);
        printf("Case #%d: %d\n", ++cas, (LL)ans);
    }


    return 0;
}

1002
题意:
给定n个字符串,找出一种从’a’-’z’到0-25的映射,使得所有字符串之和最大,但映射必须满足字符串不会出现前导0

分析:
统计所有字符串中各字符的出现次数,不同位置赋予不同权值,按权值给字符排序,最后权值和大的字符赋予大的映射值,前导0的问题,记录下所有首字符,按权值从小到大对字符进行遍历,找到第一个非首字符,将其映射值赋成0,其余字符按权值大小赋映射值大小即可。
3 Submits
第一次:没有对权值进行进位处理
第二次:数组未清零

#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<iostream>
#include<algorithm>
#define LL unsigned long long
#define debug puts("Infinity is awesome!")
#define mm(a, b) memset(a, b, sizeof(a))
#define rep(a, b, c) for(int a=b;a<c;a++)
#define rerep(a, b, c) for(int a=b;a>=c;a--)
#define reset(a, b) memset(a, b, sizeof(a))
using namespace std;
const int Inf=1e9;
const LL MOD=1e9+7;
const int maxn=1e6+100;
const int maxl=1e5+100;
int m;
char s[maxn];
int cnt[26][maxl];
int l[maxl], r[maxl];
int a[26], vis[26], ishead[26];
int mp[26];
int maxlen;
bool cmp(int a,int b){
    if(vis[a]!=vis[b]) return vis[a]<vis[b];
    if(vis[a]==vis[b]&&!vis[a]) return true;
    for(int i=maxlen-1;i>=0;i--)
        if(cnt[a][i]!=cnt[b][i])
        return cnt[a][i]<cnt[b][i];
    return true;
}
int main(){
    int cas=0;
    int n;

    while(~scanf("%d", &n)){
        int sp=0;
        maxlen=0;
        mm(vis, 0); mm(ishead, 0);
        mm(cnt, 0);
        for(int i=0;i<n;i++){
            scanf("%s",s+sp);
            ishead[s[sp]-'a']=1;
            l[i]=sp, r[i]=sp+strlen(s+sp)-1;
            maxlen=max(maxlen, (int)strlen(s+sp));
            for(int j=l[i];j<=r[i];j++){
                cnt[s[j]-'a'][r[i]-j]++;
                vis[s[j]-'a']=1;
            }
            sp=r[i]+1;
        }
        for(int i=0;i<26;i++){
            for(int j=0;j<maxlen;j++)
            if(cnt[i][j]>=26){
                cnt[i][j+1]+=cnt[i][j]/26;
                cnt[i][j]%=26;
            }
            if(cnt[i][maxlen]) maxlen++;
            if(cnt[i][maxlen-1]>=26){
                cnt[i][maxlen]+=cnt[i][maxlen-1]/26;
                cnt[i][maxlen]%=26;
                maxlen++;
            }
        }
        for(int i=0;i<26;i++) a[i]=i;
        for(int i=0;i<26;i++)
            for(int j=i+1;j<26;j++)
            if(!cmp(a[i], a[j])) swap(a[i], a[j]);
        if(ishead[a[0]]){
            int p;
            for(int i=0;i<26;i++)
            if(!ishead[a[i]]){
                p=i;
                break;
            }
            int tmp=a[p];
            for(int i=p;i>0;i--)
                a[i]=a[i-1];
            a[0]=tmp;
        }
        for(int i=0;i<26;i++)
            mp[a[i]]=i;
//        for(int i=0;i<26;i++)
//            printf("%c %d\n", 'a'+i, mp[i]);
        LL ans=0;
        for(int i=0;i<n;i++){
            LL tmp=0;
            for(int j=l[i];j<=r[i];j++){
                tmp=tmp*26%MOD;
                int x=s[j]-'a';
                tmp=(tmp+mp[x])%MOD;
            }
            ans=(ans+tmp)%MOD;
        }
        printf("Case #%d: %lld\n", ++cas, ans);
    }


    return 0;
}

1011
题意:共有n双袜子,每天早上都选干净区袜子里编号最小的来穿,每天晚上穿过的袜子放入待洗区,当待洗的袜子达到n-1双时会统一洗一次,洗完后会在次日晚上放入干净区。
问第k天穿的袜子的编号?

分析:
观察k的范围,显然O(n)的算法都不能满足要求,那么可能O(1)?这意味着答案是有规律的。稍加分析样例发现,前n天穿的袜子分别是1~n,之后n-1天穿的是1~n-2,n-1,再之后n-1天穿的是1~n-2,n,然后后两者交替出现。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<iostream>
#include<algorithm>
#define LL unsigned long long
#define debug puts("Infinity is awesome!")
#define mm(a, b) memset(a, b, sizeof(a))
#define rep(a, b, c) for(int a=b;a<c;a++)
#define rerep(a, b, c) for(int a=b;a>=c;a--)
#define reset(a, b) memset(a, b, sizeof(a))
using namespace std;
const int Inf=1e9;
const int MOD=1e9+7;
int m;
int main(){
    int cas=0;
    LL n, k;

    while(~scanf("%lld%lld", &n, &k)){
        LL ans;
        if(k<=n) ans=k;
        else{
            k-=n;
            LL x=k/(n-1), y=k%(n-1);
            if((x%2&&!y)||(!x%2&&y)){
                if(y) ans=y;
                else ans=n-1;
            }else{
                if(y) ans=y;
                else ans=n;
            }
        }
        printf("Case #%d: %d\n", ++cas, ans);
    }


    return 0;
}

补题:
1003
题意:
定义两个点之间的路径值为这条路径上不同颜色结点的个数,求树上所有路径值之和。
分析:
暂认为n个值对所有路径都是有贡献的,再枚举颜色(1~n),求取不包含这个颜色的路径数,减之,最后可得答案。
dfs处理出所有结点的覆盖范围(L, R),
求取一种颜色c的无贡献值,枚举颜色为c的结点u,在u的子树中依次找L最小的同色结点v,那么在u、v之间的所有结点中任取两点构成的路径都是不含颜色c的。
观察下图,这里写图片描述
枚举颜色c=1时,u=1,左子树没有同色,按照上述方法不减,右子树5号的结点同色,那么减去以5号结点为根的树结点个数,右子树9号结点同色,同样减去,那么右子树中无效路径已求得。
但左子树中也存在不包含c=1的路径,可上述过程并没有减去,
解决方法是加一个虚结点0,0只连向1号点,每次枚举颜色c时,将虚结点的颜色也赋为c,那么整棵树也成为了一颗子树,在枚举到虚结点时,左子树的无效路径将被减去。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<iostream>
#include<algorithm>
#define mm(a,b) memset(a, b, sizeof(a))
#define LL long long
using namespace std;
const int maxn=2e5+50;
struct Edge{
    int to, next;
};
Edge edges[2*maxn];
int head[maxn], siz[maxn];
int ecnt;
int L[maxn], R[maxn];
int c[maxn], fa[maxn];
vector<int> pos[maxn];
vector<int>::iterator it;
int n;
void Init(){
    mm(head, -1);
    ecnt=0;
    for(int i=1;i<=n;i++) pos[i].clear();
}
void Add_Edge(int u,int v){
    edges[ecnt]=Edge{v, head[u]};
    head[u]=ecnt++;
}
void Dfs(int u,int f,int &cnt){
    L[u]=++cnt; fa[u]=f;
    siz[u]=1;
    for(int i=head[u];i!=-1;i=edges[i].next){
        int v=edges[i].to;
        if(v==f) continue;
        Dfs(v, u, cnt);
        siz[u]+=siz[v];
    }
    R[u]=cnt;
}
bool cmp(const int &a,const int &b){
    return L[a]<L[b];
}
int main(){
    int cas=0;
    int u, v;


    while(~scanf("%d", &n)){
        Init();
        for(int i=1;i<=n;i++){
            scanf("%d", &c[i]);
            pos[c[i]].push_back(i);
        }
        for(int i=1;i<=n-1;i++){
            scanf("%d%d",&u, &v);
            Add_Edge(u, v);
            Add_Edge(v, u);
        }
        Add_Edge(0, 1);
        int cnt=0;
        Dfs(0, -1, cnt);
        LL ans=1LL*n*(n-1)/2*n;
        for(int i=1;i<=n;i++){
            if(pos[i].size()==0){
                ans-=1LL*n*(n-1)/2;
                continue;
            }
            pos[i].push_back(0);
            sort(pos[i].begin(), pos[i].end(), cmp);
            for(int j=0;j<pos[i].size();j++){
                u=pos[i][j];
                for(int k=head[u];k!=-1;k=edges[k].next){
                    int v=edges[k].to;
                    if(v==fa[u]) continue;
                    int size=siz[v];
                    int id=L[v];
                    while(1){
                        L[n+1]=id;
                        it=lower_bound(pos[i].begin(), pos[i].end(), n+1, cmp);
                        if(it==pos[i].end()||L[*it]>R[v]) break;
                        size-=siz[*it];
                        id=R[*it]+1;
                    }
                    ans-=1LL*size*(size-1)/2;
                }
            }
        }
        printf("Case #%d: %lld\n", ++cas, ans);
    }
    return 0;
}

1008
题意:
给出一个函数,按照该函数能够构造出前n项的值,m个询问,查询第bi小的值。

分析:
首先m是不大的,其次,bi彼此之间还存在一定的关系,当满足bi!=bj,bi<bk且bj< bk时,bi+bj<=bk,那么最差的情况下b取值为0,1,2,3,5,8…斐波那契…,那么sum(bi)的大小可以估计是O(n)的。
利用STL中nth_element函数,其平均复杂度为O(n),这里的n是指查找的范围长度,不是题目给出的n。我们先查找大的bi,可以做到逐步缩小范围,所以总体复杂度仍是O(n)。
nth_element输入3个参数,起始位置,需要查找的第k小,终止位置。

nth_element用法

#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<iostream>
#include<algorithm>
#define mm(a,b) memset(a, b, sizeof(a))
#define LL long long
using namespace std;
unsigned x, y, z;
unsigned rng61(){
    unsigned t;
    x^=x<<16;
    x^=x>>5;
    x^=x<<1;
    t=x;
    x=y;
    y=z;
    z=t^x^y;
    return z;
}
const int maxp=105;
int pos[maxp], id[maxp];
const int maxn=1e7+50;
unsigned a[maxn], ans[maxp];
bool cmp(const int &a,const int &b){
    return pos[a]<pos[b];
}
int main(){
    int cas=0;
    int n, m;
    unsigned A, B, C;

    while(~scanf("%d%d%u%u%u",&n, &m, &A, &B, &C)){
        x=A, y=B, z=C;
        for(int i=0;i<m;i++){
            scanf("%d",&pos[i]);
            id[i]=i;
        }
        for(int i=0;i<n;i++){
            a[i]=rng61();
        }
        sort(id, id+m, cmp);
        pos[id[m]=m]=n;
        for(int i=m-1;i>=0;i--){
            if(pos[id[i]]==pos[id[i+1]]){
                ans[id[i]]=ans[id[i+1]];
                continue;
            }
            nth_element(a, a+pos[id[i]], a+pos[id[i+1]]);
            ans[id[i]]=a[pos[id[i]]];
        }
        printf("Case #%d: ", ++cas);
        for(int i=0;i<m;i++)
            printf("%u%c", ans[i], i==m-1?'\n':' ');
    }
    return 0;
}

1012
题意:
给出n个区间,1~n的排列P满足pi只在区间[Li, Ri]内是最小值,一旦超出这个范围便不再是最小值,这样的排列有多少种?

分析:
首先肯定存在一个区间[1,n], 那么这个区间的编号i把区间[1,n]分成了[1,i-1]和[i+1,n],同理给定区间中也必须存在这两个区间,否则题目是无解的。可以通过递归寻找合法区间。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define PI acos(-1.0)
#define LL long long
#define ULL unsigned long long
#define INF 0x3f3f3f3f
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
// 0x3f3f3f3f3f3f3f3f
// 0x3f3f3f3f
namespace fastIO {
    #define BUF_SIZE 100000
    //fread -> read
    bool IOerror = 0;
    inline char nc() {
        static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
        if(p1 == pend) {
            p1 = buf;
            pend = buf + fread(buf, 1, BUF_SIZE, stdin);
            if(pend == p1) {
                IOerror = 1;
                return -1;
            }
        }
        return *p1++;
    }
    inline bool blank(char ch) {
        return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
    }
    inline void read(int &x) {
        char ch;
        while(blank(ch = nc()));
        if(IOerror)
            return;
        for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
    }
    #undef BUF_SIZE
};
using namespace fastIO;
const int maxn=1e6+50;
const LL mod=1e9+7;
struct Node{
    int L, R, len, id;
    bool operator <(const Node &rhs)const{
        if(L==rhs.L) return len>rhs.len;
        return L<rhs.L;
    }
};
Node nod[maxn];
vector<Node> st[maxn];
LL fac[maxn], inv[maxn];
LL ans;
int BinSearch(int s,int len){
    if(st[s].size()==0) return -1;
    int l=0, r=st[s].size()-1;
    while(l<=r){
        int m=(l+r)>>1;
        if(st[s][m].len==len) return m;
        if(st[s][m].len<len) l=m+1;
        else r=m-1;
    }
    return -1;
}
LL PowMod(LL x,int p){
    LL ret=1;
    while(p){
        if(p&1) ret=ret*x%mod;
        p>>=1;
        x=x*x%mod;
    }
    return ret;
}
void PreProcess(){
    fac[0]=inv[0]=1;
    for(int i=1;i<maxn;i++)
        fac[i]=fac[i-1]*i%mod;
    inv[maxn-1]=PowMod(fac[maxn-1], mod-2);
    for(int i=maxn-2;i>=0;i--)
        inv[i]=inv[i+1]*(i+1)%mod;
}
LL Com(int i,int j){
    return fac[i]*inv[j]%mod*inv[i-j]%mod;
}
bool flag;
bool Solve(int L,int R){
    if(!flag) return false;
    if(L>R) return true;
    int len=R-L+1;
    int pos=BinSearch(L, len);
    if(pos==-1) return flag=false;
    //printf("%d %d %d\n",L, R, st[L][pos].id);
    if(!Solve(L, st[L][pos].id-1)) return flag=false;
    if(!Solve(st[L][pos].id+1, R)) return flag=false;
    ans=ans*Com(len-1, st[L][pos].id-L)%mod;
    return true;
}
int main(){
    int cas=0;
    int n;

    //freopen("1012.in", "r", stdin);
    //freopen("a.out", "w", stdout);
    //scanf("%d",&T);
    PreProcess();
    while(read(n),!IOerror){
        for(int i=0;i<n;i++){
            read(nod[i].L);
            st[i+1].clear();
        }
        for(int i=0;i<n;i++){
            read(nod[i].R);
            //scanf("%d%d",&nod[i].L, &nod[i].R);
            nod[i].len=nod[i].R-nod[i].L+1;
            nod[i].id=i+1;
        }
        sort(nod, nod+n);
        for(int i=n-1;i>=0;i--){
            st[nod[i].L].push_back(nod[i]);
        }
        ans=1; flag=true;
        if(!Solve(1, n)) ans=0;
        printf("Case #%d: %lld\n",++cas, ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值