【A】水题,就是要注意特判,真是被自己聪明了一把,以为12点计时的时候12:00不合法,其实是合法的,被连续hack2次,整场崩溃。

【复杂度】O(1)

【代码君】

char s[10];
int main()
{
    int n;
    cin>>n;
    cin>>s;
    int x = (s[0]-'0')*10+(s[1]-'0');
    int y = (s[3]-'0')*10+(s[4]-'0');
    if(n==12){
        if(x>12){
            if(s[1] == '0') s[0] = '1';
            else s[0] = '0';
        }
        if(x == 0) s[0] = '1';
        if(y>59) s[3]='1';
        cout<<s<<endl;
    }
    else{
        int ans = 0;
        if(x==0 && y==0){
            cout<<s<<endl;
            return 0;
        }
        if(x>23){
            if(s[1] == '0') s[0] = '1';
            else s[0] = '0';
        }
        if(y>59) s[3]  ='1';
        cout<<s<<endl;
    }
}

【B】水题,判断元音字母的个数是否等于每一个a[i]即可。

【复杂度】O(n)

【C】两种解法

【解法1】set在线乱搞,定义两个集合set<int> s1, multise<int> s2, s1存的是破化的元素的下标,s2存的是区间的和。sum[]数组存的是元素前缀和.每次破化一个元素x时,在s1中找到大于x最小的已被破化的元素下标p2, 和小于x最大的已被破化的下标p1, 那么x > p1 && x < p2 所以(p1, p2)区间被分成连个区间,把这两个区间的值存到s2中,删除(p1, p2)整个区间的值,输出s2中最大值。

【复杂度】O(nlogn)

【代码君】

const int maxn = 1e6+10;
set<int>s1;
multiset<long long>s2;
long long sum[maxn];
int main()
{
    int n,x;
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) scanf("%lld",&sum[i]),sum[i] += sum[i-1];
    for(int i = 1; i <= n; i++){
        scanf("%d",&x);
        if(i == 1){
            s1.insert(x);
            s2.insert(sum[n]-sum[x]);
            s2.insert(sum[x-1]);
            printf("%lld\n",*(--s2.end()));
        }
        else{
            int h1,h2;
            auto it = s1.upper_bound(x);
            if(it == s1.end()){
                h2 = n;
                h1 = *(--it);
            }
            else{
                h2 = *it - 1;
                if(it == s1.begin()) h1 = 0;
                else h1 = *(--it);
            }
            s1.insert(x);
            s2.insert(sum[h2] - sum[x]);
            s2.insert(sum[x-1] - sum[h1]);
            s2.erase(s2.find(sum[h2] - sum[h1]));
            printf("%lld\n",*(--s2.end()));
        }
    }
    return 0;
}

【解法2】离线+带权并查集,考虑到在线删除比较麻烦,我们可以倒着来把元素加到原来的序列里面并维护最大的连续和,这个用并查集可以做。每一次判断他的前一个以及后一个是否被加进来,合并集合,更新答案即可。

【代码君】

const int maxn = 1e6+10;
long long a[maxn],ans[maxn],s[maxn];
int p[maxn],vis[maxn],fa[maxn],n;
void init(){
    memset(vis,false,sizeof(vis));
    for(int i = 1; i <= n; i++) fa[i] = i;
}
int Find(int x){
    if(x == fa[x]) return x;
    return fa[x] = Find(fa[x]);
}
int main()
{
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
    for(int i = 1; i <= n; i++) scanf("%d",&p[i]);
    init();
    ans[n] = 0;
    for(int i = n; i > 1; i--){
        int now = p[i];
        s[now] = a[now];
        if(vis[now-1]){
            int x = Find(now),y = Find(now-1);
            fa[x] = y;
            s[y] += s[x];
        }
        if(vis[now+1]){
            int x = Find(now),y = Find(now+1);
            fa[x] = y;
            s[y] += s[x];
        }
        ans[i-1] = max(ans[i],s[Find(now)]);
        vis[now] = 1;
    }
    for(int i = 1; i <= n; i++) printf("%lld\n",ans[i]);
}

【D】一个数x,可以变成2x,或者变成2x+1,可以变化若干次,现在给你n个不同的数Y,你需要找到n个不同的x,使得这n个不同的x经过变化之后,能够得到Y数组,你要使得最初的最大值最小。输出n个不同的x。

【解题方法】贪心,每次选择最大的数,然后使得最大数变小即可,能变就变,用一个set去维护就好了。

【代码君】

set<int>S;
int main()
{
    int n,x;
    scanf("%d",&n);
    for(int i = 1; i <= n; i++){
        scanf("%d",&x);
        S.insert(-x);
    }
    while(1)
    {
        int x = -*S.begin();
        int k = *S.begin();
        x /= 2;
        while(x)
        {
            if(S.find(-x) == S.end()){
                S.insert(-x);
                break;
            }
            x >>= 1;
        }
        if(x == 0){
            for(auto it = S.begin(); it != S.end(); it++){
                cout<< -*it <<" ";
            }
            return 0;
        }
        S.erase(k);
    }
    return 0;
}