CodeForces Round #705 总结&题解

突然发现博客咕了好久了,今天更新一下~~

A. Anti-knapsack

原题链接

给定整数 \(n,k\),从\(\{1,2,3,\cdots,n\}\) 中选出最大的子集 \(S\),使得 \(\nexists A\subseteq S,\sum\limits_{x \in A}x=k\)

显然,\(\gt k\) 的随便选,\(\lt k\) 的选 \(\lceil\frac{k}{2}\rceil,\lceil\frac{k}{2}\rceil+1,\cdots,k-1\) 即可。可以从可行性及最优性两方面证明。

Code:(考场写的,比较潦草)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read(){
    T 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<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
 
#define rdi read<int>
#define rdl read<ll>
 
int n,k,T;
int main(){
    T=rdi();
    while(T--){
        n=rdi(),k=rdi();
        int cnt=0,a[1010]={0};
        for(int i=k+1;i<=n;i++) a[++cnt]=i;
        for(int i=(k+1)/2;i<k;i++) a[++cnt]=i;
        printf("%d\n",cnt);
        for(int i=1;i<=cnt;i++) printf("%d ",a[i]);
        puts("");
    }
    return 0;
}

B. Planet Lapituletti

原题链接

假设一天有 \(h\) 小时,每小时 \(m\) 分钟。给定一个起始时间 (HH:MM形式),求从起始时间起第一个合法的镜像时间。

合法时间:该时间以数位管形式显示时,\(0 \le 分钟数 \lt m\)\(0 \le 时钟数 \lt m\)

合法镜像时间:该时间是合法时间,且该时间在镜子中投影(即左右翻转)后仍可以作为一个合法时间。

打表出 \(0\sim 9\) 对应镜像数字的表(不合法为 \(-1\)),然后枚举时间即可。

一个坑:\(00:01\) 可以到 \(00:00\)(相当于到了第二天)。

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
template<typename T>
inline T read(){
    T 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<<1)+(x<<3)+(ch^48);
           ch=getchar();
        }
    return x*f;
}
 
#define rdi read<int>
#define rdl read<ll>
 
int T,n,m,x,y;
const int fx[]={0,1,5,-1,-1,2,-1,-1,8,-1};
pii rev(int x,int y){
    int a=x/10,b=x%10,c=y/10,d=y%10;
    if(fx[a]==-1||fx[b]==-1||fx[c]==-1||fx[d]==-1) return make_pair(-1,-1);
    return make_pair(fx[d]*10+fx[c],fx[a]+fx[b]*10);
}
 
int main(){
    T=rdi();
    while(T--){
        n=rdi(),m=rdi();
        scanf("%d:%d",&x,&y);
        for(;;y++,(x+=y/m)%=n,y%=m){
            if(rev(x,y).first==-1) continue;
            if(rev(x,y).first<n&&rev(x,y).second<m){
                printf("%02d:%02d\n",x,y);
                break;
            }
        }
    }
    return 0;
}

C. K-beautiful Strings

原题链接

给定一个长度为 \(n\) 的字符串 \(s\) 及一个常数 \(k\),求字典序大于等于 \(s\) 、长度为 \(n\) 的字典序最小的好的字符串。

好的字符串:由小写字母构成,且每种字符都出现了 \(k\) 的倍数次。

既然要求字典序大于等于 \(s\) 且字典序最小,那么可以从后往前枚举前缀。

假设当前枚举到了 \(i\),先从 \(s_i\) 开始枚举当前这一位取什么字符,然后统计 \(s_{1\dots i}\) 中每种字符的出现次数,设为 \(cnt\),那么显然,后面 \(s_{i+1\dots n}\) 要补充 \(sum=\sum\limits_{i='a'}^{'z'} ((k-cnt_i\bmod k)\bmod k)\) 个字符,此时存在合法字符串,当且仅当 \(sum \le n-i\) 且 $ sum \equiv n-i \pmod k$。如果满足,我们就先填充 \(sum-(n-i)\) 个字符 ‘a’ ,然后从 ‘a’ 到 ‘z’ 依次处理当前字符需填充数量并填充即可。

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read(){
    T 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<<1)+(x<<3)+(ch^48);
           ch=getchar();
        }
    return x*f;
}
 
#define rdi read<int>
#define rdl read<ll>
 
const int N=100010;
int T,n,k;
char s[N];
int main(){
    T=rdi();
    while(T--){
        n=rdi(),k=rdi(),scanf("%s",s+1);
        if(n%k){
            puts("-1");
            continue;
        }
        int cnt[30]={0};
        for(int i=1;i<=n;i++) cnt[s[i]-'a']++;
        bool flg=1;
        for(int j=0;j<26;j++) flg&=((cnt[j]%k)==0);
        if(flg){
            printf("%s\n",s+1);
            continue;
        }
        for(int i=n;i>=1;i--){
            int cur=s[i]-'a',success=0;
            for(int j=cur+1;j<=26;j++){
                cnt[cur]--;cnt[j]++;s[i]=j+'a';
                int kkk=0;
                for(int j=0;j<26;j++){
                    if(cnt[j]%k) kkk+=(k-cnt[j]%k)%k;
                }
                if(kkk<=n-i&&(n-i-kkk)%k==0){
                    int pos=i;
                    for(int o=1;o<=(n-i-kkk)-(n-i-kkk)%k;o++) s[++pos]='a';
                    for(int o=0;o<26;o++){
                        if(cnt[o]%k){
                            for(int p=1;p<=(k-cnt[o]%k);p++) s[++pos]=o+'a';
                        }
                    }
                    success=1;
                    goto ed;
                }
                cnt[cur]++,cnt[j]--;s[i]=cur+'a';
            }
            cnt[s[i]-'a']--;
        }
//        char cur=s[0]+1;
  //      for(int i=1;i<=k;i++) s[i]=
ed:
        printf("%s\n",s+1);
    }
    return 0;
}
 

D. CF1493D GCD of an Array

原题链接

给定一个序列 \(a\),要求支持每次将某个位置 \(i\) 乘上一个数并询问当前整个序列的 \(\gcd\)

先筛一下质数,开一些 \(multiset\) ,第 \(i\) 个维护 \(a\) 中每个数分解质因数后 \(1\sim 2\times10^5\) 中第 \(i\) 大的质数的指数。

修改将要乘的数分解质因数后直接加上即可。但查询若把 \(1\sim 2\times10^5\) 每个质数都查询一次最小值肯定会TLE,所以要维护答案的变化量,只查询被修改过的对应的质数。

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
template<typename T>
inline T read(){
    T 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<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
 
#define rdi read<int>
#define rdl read<ll>
 
const int N=200010,MOD=1e9+7,INF=0x3f3f3f3f,P=18010;
int n,q,a[N];
 
int p[N],cp,vis[N];
void pr(int lim){
    vis[0]=vis[1]=1;
    for(int i=2;i<=lim;i++){
        if(!vis[i]){
            p[++cp]=i;
            for(int j=i;j<=lim/i;j++) vis[i*j]=1;
        }
    }
}
 
ll qpow(ll x,ll y){
    ll res=1;
    while(y){
        if(y&1) (res*=x)%=MOD;
        (x*=x)%=MOD;y>>=1;
    }
    return res;
}
 
multiset<int> s[P];
map<int,int> fac[N],tmp;
ll ans=1;
ll gcd(ll x,ll y){return !x?y:gcd(y%x,x);}
int main(){
    n=rdi(),q=rdi();
    for(int i=1;i<=n;i++){
        a[i]=rdi();
        if(i==1) ans=a[i];
        else ans=gcd(ans,a[i]);
    }
    pr(N-10);
    for(int i=1;i<=n;i++){
        int lim=sqrt(a[i]),x=a[i];
        for(int j=1;p[j]<=lim&&j<=cp;j++){
            if(x%p[j]==0){
                int cnt=0;
                while(x%p[j]==0) x/=p[j],cnt++;
                fac[i][j]=cnt;
            }
            lim=sqrt(x);
        }
        if(x>1){
            int tmp=lower_bound(p+1,p+cp+1,x)-p;
            fac[i][tmp]++;
        }
        for(auto x:fac[i]) s[x.first].insert(x.second);
    }
    while(q--){
        int id=rdi(),val=rdi();
        int lim=sqrt(val),x=val;tmp.clear();
        for(int j=1;p[j]<=lim&&j<=cp;j++){
            if(x%p[j]==0){
                int cnt=0;
                while(x%p[j]==0) x/=p[j],cnt++;
                tmp[j]=cnt;
            }
            lim=sqrt(x);
        }
        if(x>1){
            int posss=lower_bound(p+1,p+cp+1,x)-p;
            tmp[posss]++;
        }
        for(auto x:tmp){
            int x1=x.first,x2=x.second;
            int pre=fac[id][x1],post=pre+x2;
            fac[id][x1]+=x2;
            int minx1=s[x1].size()<n?0:(*s[x1].begin());
            if(pre) s[x1].erase(s[x1].find(pre));
            s[x1].insert(post);
            int minx2=s[x1].size()<n?0:(*s[x1].begin());
            ans=ans*qpow(p[x1],minx2-minx1)%MOD;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

完结撒花~~

posted @ 2021-03-07 18:12  Zesty_Fox  阅读(108)  评论(0编辑  收藏  举报