A、Anti-knapsack

题意:
给我们一个n,一个k(n,k<=1000),需要我们得到一个集合,集合的元素全部小于n,并且任意子集相加不等于k,而且这个集合元素相加尽可能大。

思路:
大于的数全取,小于的的数只能取一半,所以取大的一半。

MyCode:

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+7;
typedef long long int ll;
#define eb emplace_back
#define ef emplace_front
#define ep emplace
#define fi first
#define se second
inline void solve() {
    int n,k;
    cin>>n>>k;
    int l=(k+1)>>1;
    if(n-l) {
        cout<<n-l<<'\n';
        for(int i=l;i<k;++i) cout<<i<<' ';
        for(int i=k+1;i<=n;++i) cout<<i<<' ';
        cout<<'\n';
    }
    else cout<<0<<'\n';
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin>>t;
    while(t--) solve();
}

B、 Planet Lapituletti

题意:
某个星球上的时间和我们地球类似,但是每天有h个小时,每小时有m分钟(1<=h,m<=100)。当前时间为HH:MM,这时候有一面镜子,问从HH:MM开始往后,当镜子里的时间合理时,即镜子里的时间满足0<=H<h && 0<=M<m时(镜子里的时间是当前时间的反射),真实时间是多少,以HH:MM格式输出。

思路:
模拟

MyCode:

#include <bits/stdc++.h>
using namespace std;
int h,m;
int mp[10]={0,1,5,-1,-1,2,-1,-1,8,-1};
inline bool check(const vector<int> &a, const vector<int> &b) {
    if(mp[a[1]]==-1||mp[a[0]]==-1||mp[b[0]]==-1||mp[b[1]]==-1) return false;
    int hh=mp[a[1]]*10+mp[a[0]],mm=mp[b[1]]*10+mp[b[0]];
    if(hh>=h||mm>=m) return false;
    return true;
}
inline void solve() {
    int hh,mm;
    scanf("%d%d",&h,&m);
    scanf("%d:%d",&hh,&mm);
    vector<int>a(2),b(2);
    while(1) {
        for(int i=hh,j;i<h;++i) {
            for(j=mm;j<m;++j) {
                a[0]=i/10,a[1]=i%10;
                b[0]=j/10,b[1]=j%10;
                if(check(b,a)) {
                    printf("%02d:%02d\n",i,j);
                    return;
                }
            }
            mm=0;
        }
        hh=0;
    }
}
int main() {
    int T;    scanf("%d",&T);
    while(T--) solve();
}

C、K-beautiful Strings

题意:
给我们一个由小写字母组成的字符串S,长度为n(n<=1e5),然后给我们一个k(k<=n),要我们找出比字典序尽可能小但不小于 S 的字符串,这个字符串还需满足每种字母出现的个数都是k的倍数。

思路:
当字符串长度n不是k的倍数的时候,不存在这种字符串
当字符串s满足条件时,s就是需要求的字符串
在修改字符串s的一些字符串从而得到需要的字符串时,开始修改的部位一定越靠后越好,这样得到的字符串才能最小。用桶存每个字母出现的次数,从大到小枚举修改的部分,计算还需要修改几(假设需要修改sum个位置)个位置才能使的字母的出现次数是k的倍数,同时用que保存出现次数不是k的倍数的字母。如果sum小于,那么就得不到需要的字符串。用nex数组维护每个字母还需要加几个才能凑齐k的倍数
情况一:当时,如果出现次数不是k的倍数的字母中最大的字母x大于s[i],那么直接把s[i]改成que中第一个大于s[i]的字母,同时修改这个字母的nex数组,以及sum--,表示已经修改了一次了;
情况二:当时,因为n是k的倍数,所以此时,也就是说我可以构造k个,所以直接把s[i]改成s[i]+1,同时修改s[i]+1的nex数组,以及sum--,需要注意的是,可能s[i]+1的nex数组已经是k的倍数了,所以如果此时可能会是负数,如果减成了负数,那么即可。
优先判断情况二(保证求得的字符串最小),处理完情况一或情况二后,把多余的字母修改成'a',然后从小到大修改成需要增加的字母。

MyCode:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+7,maxm=2e5+7;
typedef long long int ll;
#define eb emplace_back
#define ef emplace_front
#define ep emplace
#define fi first
#define se second
int n,k,cnt[30],que[30],nex[30],tot,sum;
char s[maxn];
inline bool check(int r) {
    sum=tot=0;
    for(int i=1; i<=26; ++i) if(cnt[i]%k) {
            sum+=k-cnt[i]%k;
            que[++tot]=i;
        }
    return sum<=n-r+1;
}
inline void print(int r) {
    for(int i=1; i<=r; ++i) printf("%c",s[i]);
}
int main() {
//    ios::sync_with_stdio(false);
//    cin.tie(nullptr);

    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&k);
        scanf("%s",s+1);
        if(n%k) {
            puts("-1");
            continue;
        }
        for(int i=1; i<=26; ++i) cnt[i]=0;
        for(int i=1; s[i]; ++i) cnt[s[i]-'a'+1]+=1;
        if(check(n+1)) {
            print(n);
            putchar('\n');
            continue;
        }
        for(int i=n; i; --i) {
            cnt[s[i]-'a'+1]-=1;
            if(s[i]=='z') continue;
            if(check(i) && (que[tot] > s[i]-'a'+1 || sum < n-i+1)) {
                print(i-1);
                for(int j=1; j<=26; ++j) if(cnt[j]%k) nex[j]=k-cnt[j]%k;
                    else nex[j]=0;
                if(sum < n-i+1) {
                    printf("%c",s[i]+1);
                    --nex[s[i]-'a'+2],--sum;
                    if(nex[s[i]-'a'+2]<0) nex[s[i]-'a'+2]+=k,sum+=k;
                } else {
                    for(int j=s[i]-'a'+2; j<=26; ++j) {
                        if(nex[j]) {
                            printf("%c",j+'a'-1);
                            --nex[j],--sum;
                            break;
                        }
                    }
                }
                nex[1]+=n-i-sum;
                for(int j=1; j<=26; ++j) {
                    while(nex[j]--) printf("%c",(char)(j-1+'a'));
                }
                putchar('\n');
                break;
            }
        }
    }
}

D、GCD of an Array

题意:
给一个数列,m次操作,每次将pos位置的数乘上x,问每次操作后数列的总gcd

思路:
求数列的总gcd会想到分解质因数,每个位置共有的质因数乘以这个质因数的最小个数,就是他们的总gcd。
将pos位置的数乘上x,其实就是给pos位置增加因子x,分解出x内的质因数,算贡献。

MyCode:

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+7,maxm=4e4+7,mod=1e9+7;
#define eb emplace_back
int pre[maxn],prime[maxn],rev[maxn];
int n,ans=1;
unordered_map<int,int>mp[maxn];
vector<int>v[maxm];
inline void add(int x,int val) {
    int t,y;
    while(val!=1) {
        t=pre[val];//先筛出最小的质因数 
        val/=t;
        t=rev[t];//离散化 
        y=mp[x][t]++;//y+1表示 a[x]质因数t的个数
        if(y==v[t].size())  v[t].emplace_back(0);//质因子t的最大个数增加,则新开一维去统计 
        if(++v[t][y]==n) ans=ans*1LL*prime[t]%mod;//又凑齐n个质因子t,最大公约数*t 
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    for(int i=2,tot=0;i<maxn;++i) {
        if(!pre[i]) {
            prime[++tot]=pre[i]=i;
            rev[i]=tot;
        }
        for(int j=1;j<=tot&&i*prime[j]<maxn;++j) {
            pre[i*prime[j]]=prime[j];
            if(i%prime[j]==0) break;
        }
    }
    int Q,x,val;
    cin>>n>>Q;
    for(int i=1;i<=n;++i) {
        cin>>x;
        add(i,x);
    }
    while(Q--) {
        cin>>x>>val;
        add(x,val);
        cout<<ans<<'\n';
    }
}

E、Enormous XOR

题意:
n是二进制串的长度
第一行是L的二进制表示
第二行是R的二进制表示

思路:
最差的情况就是取x=y=R,答案就是R

结论一:若L和R的最高位相同,那么答案为11...11
我只要取x=01...11,y=10...00即可。

结论二:若L和R最高位相同,R为奇数,答案是R
不会证

结论三:若L和R最高位相同,R为偶数,且,答案是R+1,反之是L
我只要取R、R-1、R-2就可以得到R+1

MyCode:

#include <bits/stdc++.h>
using namespace std;
int n,pos;
string l,r;
string add(string s) {
    pos=n-1;
    while(s[pos]=='1') {
        s[pos]='0';
        --pos;
    }
    s[pos]='1';
    return s;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin>>n;
    cin>>l>>r;
    if(l[0]!=r[0]) {
        string c(n,'1');
        cout<<c<<'\n';
        return 0;
    }
    if(l==r||r[n-1]=='1'||add(l)==r)
        cout<<r<<'\n';
    else cout<<add(r)<<'\n';
}