Codeforces Round #624 (Div. 3)

A
选正奇数 增加,正奇数减少,首先判断两个数的大小关系。看是该增加还是减少,然后答案显然只有0,1,2。

int main(){
    int t = read();
    while(t--){
        ll a,b;
        a = read();b = read();
        if(a == b) puts("0");
        else if(a < b){
            if((b - a) % 2 != 0) puts("1");
            else {
                puts("2");
            }
        }
        else{
            if((a - b) % 2 == 0) puts("1");
            else puts("2");
        }
    }
}

B
给你 n n n个数字,然后再给你 m m m个数, p 1 , p 2 . . . p m p_1,p_2...p_m p1,p2...pm,你可使任意次交换 p i , p i + 1 p_i,p_i+1 pi,pi+1处的 位置。问你是否可以排成非递减序列。
思路:
冒泡即可。只不过只能交换 p i , p i + 1 p_i,p_i+1 pi,pi+1位置的数。
O ( n ⋅ m ) O(n\cdot m) O(nm)

int a[200];
int b[200];
int main(){
    int t = read();
    while(t--){
        int n = read(),m = read();
        for(int i = 1;i <= n;++i) a[i] = read();
        for(int i = 1;i <= m;++i){
            b[i] = read();
        }
        for(int i = 1;i <= n;++i){
            for(int j = 1;j <= m;++j){
                if(a[b[j]] > a[b[j]+1]) swap(a[b[j]],a[b[j]+1]);
            }
        }
        if(is_sorted(a+1,a+n+1)) puts("YES");
        else puts("NO");
    }
}

O ( n ) O(n) O(n)做法
给定 p p p,可交换 a [ p ] a[p] a[p] a [ p + 1 ] a[p+1] a[p+1],其他的都不能发生交换,所以我们可以看成一个个小段,且当前小段的最小值要大于等于前一个小段的最大值才行,而在一个小段的内部,可能并不是有序的,所以我们只记录这个小段的最大值就行。

bool vis[200];
int a[200];
void solve(){
    memset(vis,0,sizeof vis);
    int n = read(),m = read();
    rep(i,1,n) a[i] = read();
    rep(i,1,m) {
        int b = read();
        vis[b] = 1;
    }
    bool ok = true;
    int M = 0;
    int ax = a[1];
    rep(i,2,n){
        if(!vis[i-1]) M = ax;//前一个不能交换,说明当前元素和前一个元素不是一个小段中的,取最大值
        if(M > a[i]) {ok = 0;break;}
        ax = max(ax,a[i]); 
    }
    if(ok) puts("YES");
    else puts("NO");
    
}
int main(){
    int t = read();
    while(t--){
        solve();
    }
}

C
给你一个一个字符串,里面的字母代表开关,你从头开始按,按到某一处出错后,下一次要从头开始按。给你 m个数,代表着按错的开关的位置。让你输出26个字母。表示开关被按下的次数。
思路:
开关在第 i i i个位置处出错,说明第 i i i个位置按下后,下一次要从1开始按,也就是 [ 1 , i ] [1,i] [1,i]位置的开关都多按了一次。我们不妨设有一个数组 d d d d [ i ] d[i] d[i]意思是 i i i之前的位置多按了几次。然后遍历 m m m个错误个位置累加贡献,最后倒着累加,最后统计即可。

ll f[N];
char s[N];
ll b[28];
int main(){
    int t = read();
    while(t--){
        memset(f,0,sizeof f);
        memset(b,0,sizeof b);
        int n = read(),m = read();
        scanf("%s",s+1);
        for(int i = 1;i <= m;++i){
            ll a=  read();
            f[a] ++;
        }
        for(int i = n-1;i >= 1;i--) f[i] += f[i+1];
        for(int i = 1;i <= n;++i){
            b[s[i]-'a'] += f[i] + 1;
        }
        for(int i = 0;i < 26;++i) printf("%lld ",b[i]);
        puts("");
    }
}

D
题意给你三个数 A , B , C A,B,C A,B,C,然后每个数都可以+1 / -1。让你输出A | B && B | C 需要进行的最小操作数。
思路:
1、枚举倍数
复杂度为 n l o g n nlogn nlogn级別,并非 n 2 n^2 n2级别。。。还要注意上线设大些

int res[3];
int main(){
    int t = read();
    while(t--){
        ll a = read(),b = read(),c = read();
        ll ans = INF;
        for(int i = 1;i <= 2e4;++i){
            for(int j = i;j <= 2e4;j += i){
                for(int k = j;k <= 2e4;k += j){
                   int d = abs(i-a)+abs(j-b)+abs(k-c);
                   if(ans > d) {ans = d;res[0] = i;res[1] = j;res[2] = k;}
                }
            }
        }
        cout << ans<<endl;
 
        cout << res[0] <<' '<<res[1] <<' '<<res[2]<<endl;
    }
}

2、预处理 2 e 4 2e4 2e4级别的因子,枚举 b b b,然后 O ( 1 ) O(1) O(1)得到c,然后a必为b的因子,枚举一下a。取最小。

int res[3];
vector<int> Q[N];
void init(){
    for(int i = 1;i <= 2e4;++i){
        for(int j = 1;j <= sqrt(i);++j){
            if(i % j == 0) {Q[i].push_back(j);if(i/j != j) Q[i].push_back(i/j);}
        }
    }
}
int main(){
    init();
    int t = read();
    while(t--){
        ll a = read(),b = read(),c = read();
        ll ans = INF;
        for(int i = 1;i <= 2e4;++i){
            int d = c / i;
            int c1 = d * i,c2 = (d+1)*i;
            if(abs(c1-c) > abs(c2-c)) swap(c1,c2);//c1为c
            ll sum = abs(c1 - c) + abs(i-b);
            int S = Q[i].size();
            for(int j = 0;j < S;++j){
                ll a1 = Q[i][j];//枚举a值
                if(ans > sum + abs(a-a1)) ans = sum + abs(a-a1),res[0] = a1,res[1] = i,res[2] = c1;
            }
        }
        cout << ans<<endl;
        cout << res[0] <<' '<<res[1] <<' '<<res[2]<<endl;
    }
}

E
题意:
你有 n n n个节点,然后让你构造一棵二叉树,使得各个节点的深度之和为 d d d,不能输出 N O NO NO,否则输出YES,并写出从 2 2 2 n n n节点的父亲节点。
思路:
构造题。
首先, n n n个节点的二叉树,深度之和最大的情况是树链,最小的情况是完全二叉树的情况,如果 d d d处于这个范围内肯定有解。
然后如何构造,我们从树链的情况,然后一步步往上移动最后一个节点,没上调一层,总值减一,一直到无法上调(即上一层已满),这期间判断下是否为 d d d
上述便是构造过程,我们可以设一个 c n t cnt cnt数组来记录每一层的节点个数,初始1-n层都有一个节点。经过构造后 c n t [ i ] cnt[i] cnt[i]便是第 i i i层的节点个数。最后我们在由cnt构造出二叉树,然后写出答案。
在我的构造方案里似乎,构造二叉树的过程更难一些。。。难受
构造二叉树过程不想写了。。。如果有疑问的话在说声,我在补上。

int cnt[5005];//记录每一层的节点个数
int ax[5005];//每一层节点最多有多少
int pre[5005];//储存答案
int temp[2][5005];//这个是为了构造二叉树用的
void solve(){
    memset(cnt,0,sizeof cnt);
    int n = read(),d = read();
    int num = (n-1)*n/2;//树链
    rep(i,1,n) cnt[i] ++;
    if(d > num) {puts("NO");return ;}
    ax[1] = 1;
    rep(i,2,15) ax[i] = ax[i-1]*2;
    int p = n;
    bool ok = true;
    while(true){//构造过程
        if(num <= d) break;
        int idx = p,tmp = num;
        ok = true;//最下层节点能否上移
        while(cnt[idx-1]!=ax[idx-1]&& tmp > d){
            idx --; tmp --; ok = false;
        }
        if(ok) break;
        cnt[idx]++;cnt[p--]--;num = tmp;
    }
    if(num != d) puts("NO");
    else {
        puts("YES");
        ok = false;
        temp[!ok][1] = 1;
        int tot = 2;int inx = 1;
        while(cnt[tot]){
            int op  = 0;p = 1;
            for(int i = 1;i <= cnt[tot];i ++){//构造二叉树
                temp[ok][i] = ++inx;
                if(op == 0) pre[inx] = temp[!ok][p];
                else  pre[inx] = pre[inx-1],++p;
                op = (++op)%2;
            }
            ok = !ok;tot ++;
        }
        rep(i,2,n) cout << pre[i]<<' ';
        puts("");

    }

}
int main(){
    int  t = read();
    while(t--){
        solve();
    }
}

F
题意:
n n n个点在 X X X轴上,初始坐标值分别为 a 1 , . . . . a n a_1,....a_n a1,....an,然后每个点都有一个速度 v i v_i vi,定义 d ( i , j ) d(i,j) d(i,j)是经过 t t t秒后,两个点的最小距离。让你求 ∑ 1 < = i < j < = n n d ( i , j ) \sum\limits_{1<=i1<=i<j<=nnd(i,j)

思路:
我们考虑对答案的贡献。显然对于 x i , x j x_i,x_j xi,xj,不失一般性设 x i < x j x_ixi<xj,如果 v i > v j v_i>v_j vi>vj,的话两个点迟早会相遇,也就是 d ( i , j ) = 0 d(i,j)=0 d(i,j)=0,只有 v i < = v j v_i<=v_j vi<=vj,才有有贡献,且贡献为 x j − x i x_j-x_i xjxi
树状数组 +离散化
我们按 a i a_i ai从小到大排序,对每个点,我们只需要查询比 a i a_i ai的点且小于等于 v i v_i vi的点,那么答案的贡献是比他小的点的个数 ⋅ a i − S u m \cdot a_i-Sum aiSum,其中 S u m Sum Sum为小于等于 v i v_i vi a i a_i ai的和值。

struct node{
    int a,b;
}res[N];
bool cmp(node A,node B){
    return A.a < B.a
}
int b[N];ll c[N][2];
int n;
void add(int x,int num){
    for(;x <= n;x += x&(-x)) c[x][0] ++,c[x][1] += num;
}
ll ask(int x,int k){
    ll ans = 0;
    while(x > 0){
        ans+= c[x][k];
        x -= x&(-x);
    }
    return ans;
}
int main(){
    n = read();
    rep(i,1,n) res[i].a = read();
    rep(i,1,n) b[i] = res[i].b = read();
    sort(res+1,res+n+1,cmp);
    sort(b+1,b+n+1);
    int m = unique(b+1,b+n+1) - b - 1;
    ll ans = 0;
    rep(i,1,n){
        int x = lower_bound(b + 1,b +m + 1,res[i].b) - b;
        ans += res[i].a*ask(x,0) - ask(x,1);
        add(x,res[i].a);
    }
    cout << ans;
}

你可能感兴趣的:(Contest)