2021 BUAA Winter Training 5

题意

给你一个小写字母的字符串,问这个祖字符串中每个字母是否只出现了一次,且所有出现的字母连续。

多组询问。

解法

,瞎暴力都能过。。。

暴力大法好!

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 110
using namespace std;
int len,num[26];
char str[MAXN];
inline int read(){
    int date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    return date*w;
}
bool solve(){
    for(int i=1;i<=len;i++)num[str[i]-'a']++;
    for(int i=0;i<=25;i++)if(num[i]>1)return false;
    int l=0,r;
    while(num[l]==0&&l<=25)l++;
    r=l;
    while(num[r]==1&&r<=25)r++;
    if(r>25)return true;
    l=r;
    while(num[l]==0&&l<=25)l++;
    if(l<=25)return false;
    else return true;
}
void init(){
    scanf("%s",str+1);
    len=strlen(str+1);
    memset(num,0,sizeof(num));
}
int main(){
    int t=read();
    while(t--){
        init();
        if(solve())printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

题意

给你个数字的数列,要求分成若干段,每段中奇数和偶数的个数相同,每次分割的代价是分割两侧数字的差的绝对值。

求在代价不超过某个预算值且代价花费最大的情况下,最多能分多少段。

解法

显然我们可以先把所有能分割的位置搞出来,然后按照代价丢进一个小根堆里,每次取出堆顶即可。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<queue>
#include<vector>
#define MAXN 110
using namespace std;
priority_queue<int,vector<int>,greater<int>> q;
int n,m,ans=0,val[MAXN],num[MAXN];
char tp[100000],*p1=tp,*p2=tp;
inline char get_char(){
    return p1==p2&&(p2=(p1=tp)+fread(tp,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    int date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=get_char();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=get_char();}
    return date*w;
}
void work(){
    while(!q.empty()){
        int x=q.top();
        q.pop();
        if(x>m)break;
        m-=x;
        ans++;
    }
    printf("%d\n",ans);
}
void init(){
    n=read();m=read();
    num[0]=0;
    for(int i=1;i<=n;i++){
        val[i]=read();
        if(val[i]&1)num[i]=num[i-1]+1;
        else num[i]=num[i-1]-1;
        if(i>=2&&num[i-1]==0)q.push(abs(val[i-1]-val[i]));
    }
}
int main(){
    init();
    work();
    return 0;
}

题意

给一颗个点的根为(首都)的树,把个点选为工业城市,剩下的为旅游城市,求所有工业城市到首都的路径上旅游城市的个数的总和。

解法

首先首都肯定是旅游城市。

其次旅游城市一定要尽可能地靠近首都,这样才更可能被重复计数。

然后把问题转化一下:如果所有的城市都是工业城市,那么我们只要选出个旅游城市即可。

这和原问题不是差不多么?!

但是这样我们就能考虑假如把一个城市设为旅游城市,对答案的贡献是多少。

很容易发现这个贡献就是

所以我们把每个点按照排个序,从大到小选个即可。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 200010
using namespace std;
int n,m,c=1;
int head[MAXN],deep[MAXN],size[MAXN],pos[MAXN];
long long ans=0;
struct Tree{
    int next,to;
}a[MAXN<<1];
char tp[100000],*p1=tp,*p2=tp;
inline char get_char(){
    return p1==p2&&(p2=(p1=tp)+fread(tp,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    int date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=get_char();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=get_char();}
    return date*w;
}
inline bool cmp(const int &x,const int &y){
    return size[x]-deep[x]>size[y]-deep[y];
}
void add_edge(int x,int y){
    a[c].to=y;a[c].next=head[x];head[x]=c++;
    a[c].to=x;a[c].next=head[y];head[y]=c++;
}
void dfs(int rt){
    size[rt]=1;
    for(int i=head[rt];i;i=a[i].next){
        int will=a[i].to;
        if(!deep[will]){
            deep[will]=deep[rt]+1;
            dfs(will);
            size[rt]+=size[will];
        }
    }
}
void work(){
    for(int i=1;i<=n;i++)pos[i]=i;
    sort(pos+1,pos+n+1,cmp);
    for(int i=1;i<=n-m;i++)ans+=1LL*(size[pos[i]]-deep[pos[i]]);
    printf("%lld\n",ans);
}
void init(){
    n=read();m=read();
    for(int i=1,x,y;i<n;i++){
        x=read();y=read();
        add_edge(x,y);
    }
    deep[1]=1;
    dfs(1);
}
int main(){
    init();
    work();
    return 0;
}

题意

有一个长度为,各个数字值域为的全排列序列,给出序列,请求出序列。保证有解。

解法

对于一开始序列最后一个数最后一个数的值必为

但是对于之前的数,它有可能大于,这使得这个位置上的值不是,而是少了一个

所以我们需要从后往前推,并且动态维护前缀和。

前缀和?树状数组,请开始你的表演。

可以用树状数组维护的

找最后那个未知的数在外面套一个二分就行了。

复杂度,树状数组常数极小,还是能轻松过的。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 200010
using namespace std;
int n,ans[MAXN];
long long val[MAXN],sum[MAXN];
char tp[100000],*p1=tp,*p2=tp;
inline char get_char(){
    return p1==p2&&(p2=(p1=tp)+fread(tp,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline long long read(){
    long long date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=get_char();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=get_char();}
    return date*w;
}
inline int lowbit(int x){return x&(-x);}
inline void update(int x,int v){for(;x<=n;x+=lowbit(x))sum[x]+=v;}
inline long long query(int x){long long s=0;for(;x;x-=lowbit(x))s+=sum[x];return s;}
void work(){
    for(int i=n,l,r,mid,s;i>=1;i--){
        l=1;r=n;s=0;
        while(l<=r){
            mid=l+r>>1;
            if(query(mid-1)<=val[i]){
                s=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        update(s,-s);
        ans[i]=s;
    }
    for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    printf("\n");
}
void init(){
    n=read();
    for(int i=1;i<=n;i++){
        val[i]=read();
        update(i,i);
    }
}
int main(){
    init();
    work();
    return 0;
}

题意

给你一堆素数,求这些数的乘积的所有因子的乘积的值。

解法

因为给出的都是素数,所以我们不用质因数分解了,直接排序去重,统计每个数出现多少次即可。

对于每个数,我们知道它出现了次,可以取它的次方。

而其他数的所有取法有种。

所以我们设:

那么对答案的贡献就是:

最终答案就是:

但是那个幂次太大了,也无能为力啊。。。

这个时候就要拿出费马小定理了:

时,

所以:

这样就完成了。

一开始有个地方没开直接,药丸。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 200010
#define MOD 1000000007LL
using namespace std;
int n,val[MAXN],num[MAXN];
long long ans=1,f[MAXN],g[MAXN],s[MAXN];
char tp[100000],*p1=tp,*p2=tp;
inline char get_char(){
    return p1==p2&&(p2=(p1=tp)+fread(tp,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline long long read(){
    long long date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=get_char();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=get_char();}
    return date*w;
}
long long mexp(long long a,long long b,long long c){
    long long s=1;
    while(b){
        if(b&1)s=s*a%c;
        a=a*a%c;
        b>>=1;
    }
    return s;
}
void work(){
    f[0]=1;
    for(int i=1;i<=n;i++)f[i]=f[i-1]*(num[val[i]]+1)%(MOD-1);
    g[n+1]=1;
    for(int i=n;i>=1;i--)g[i]=g[i+1]*(num[val[i]]+1)%(MOD-1);
    for(int i=1;i<=n;i++){
        long long k=f[i-1]%(MOD-1)*g[i+1]%(MOD-1)*(1LL*(1LL*num[val[i]]*(num[val[i]]+1)/2)%(MOD-1))%(MOD-1);
        ans=ans*mexp(val[i],k,MOD)%MOD;
    }
    printf("%lld\n",ans);
}
void init(){
    memset(num,0,sizeof(num));
    n=read();
    for(int i=1;i<=n;i++){
        val[i]=read();
        num[val[i]]++;
    }
    sort(val+1,val+n+1);
    n=unique(val+1,val+n+1)-val-1;
}
int main(){
    init();
    work();
    return 0;
}