Codeforces Round #696 (Div. 2) 2021-01-19

时间:2021-01-28 12:11:49   收藏:0   阅读:0

 

https://codeforces.com/contest/1474

 

A√

题意:
现有两个长度为n的0,1字符串a,b(其中仅包含0,1),对两个字符串相加得到字符串c(按数值不进位相加);
例如:
a=“011011”;
b=“101101”;
则c=“112112”;
之后将c中的连续重复数字替换为一位得到字符串d,对于上述c它的对应字符串d=“1212”;

输出:题目给出一个字符串b;求一个字符串a使得a+b对应的字符串d最大;

技术图片
#include <bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        string s;
        int num;
        scanf("%d",&num);
        int a[num]={0};
        int flag[num]={0};
        cin>>s;
        for(int i=0;i<num;i++)
        {
            if(i==0) 
            {
                a[i]=1;
                flag[i]=1;
            }
            else{
                if(((s[i]==s[i-1])&&!flag[i-1])||((s[i-1]==0)&&(s[i]==1))||((s[i-1]==1)&&(s[i]==0)&&(flag[i-1]==1)))
                {
                    a[i]=1;
                    flag[i]=1;
                }
                else{
                    a[i]=0;
                }
            }
        }
        for(int i=0;i<num;i++)
        printf("%d",a[i]);
        printf("\n");
    }
    
    return 0;
  }  
View Code

 

B√

题意:
题意比较简单
给了一个整数d让我们找出最小的正整数a,使得a至少有4个因子且a的任意两个因数之差至少为d。

解析:
1:对于一些素数p和q,整数有4个约数,如果它的形式是p×q或p3。在第一种情况下,它有1,p, q, pq。
在第二种情况下,它有因子1 p p2 p3。
2:根据贪心,本题不是找到至少有4个约数的整数,而是找到恰好有4个约数的整数(任意两个因数相差大于等于d)。
3:若p为a最小的质因数,要使得任意两个因数只差至少为d则p≥d+1。
则解法为:
四个因数,第一个为1,第四个为a,则中间两个就为第一个比1大于等于d的质数和第一个比第二个因数大于等于的质数,将它们相乘即可得到 a
即:1,d+m,d+m+n,(d+m)
(d+d+m)(m为找不到恰好相差d的时候找到的第一个大于d的质数-d,n同理)

^^^

lower_bound( )和upper_bound( )都是利用二分查找的方法,左闭右开的有序区间里进行二分查找的。
两个函数在algorithm头文件下;

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字;

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字;

找到返回该数字的地址,不存在则返回end

技术图片
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;    
const int N=200005;

bool vis[N];
ll primer[N];//存素数
int cnt=0;
void find_primer(){//欧拉筛
    for(ll i=2;i<=N;i++){
        if(!vis[i])primer[cnt++]=i;
        
        for(ll j=0;j<cnt&&primer[j]*i<=N;j++){
            vis[i*primer[j]]=1;

            if(i%primer[j]==0)break;
        }
    }
}

int main()
{
    string s,c;
    int T;
    cin>>T;
    int n;
    find_primer();
    // for(int i=0;i<=10000;i++){
    //     cout<<primer[i]<<endl;
    // }
    while(T--){
        scanf("%d",&n);//差值n
        ll ans=0;
        ll a=upper_bound(primer,primer+cnt,n)-primer;//二分查找找到第一个差值为n的质数;
       // cout<<"**"<<a<<endl;
        a=primer[a];
     //    cout<<"****"<<a<<endl;
        ll b=lower_bound(primer,primer+cnt,a+n)-primer;//二分查找找到第二个差值为n的质数
        b=primer[b];
        ans=a*b;//相乘求得ans的值
        cout<<ans<<endl;
    } 
    return 0;
}
View Code

 

C

题目:
给出2n个数,初始可以指定任意的数作为基数,然后将数组中和为基数的两个数删除,较大的数作为新的基数,问如此往复是否可以删除所有的数,并输出每轮的基数

解析:

  1. 通过推理,如果删除最大的数,那么基数一定是比这个最大的数和其他任意数的组合,在删除完后最大的数成为了基数,那么如果现存数中最大的数无法被删除,那么基数一定会被一个比现存最大的数更小的数所替代,那么这个现存数种最大的数永远无法被删除
  2. 那么就可以得知除了第一轮的基数(即最大的数和其他数的组合)是无法确定的以外,后续结果都已经固定(必须删除次大的数),检验过程通过检测现存的数中是否存在基数减去最大数的值即可,因此进行O(n)的检验是否出现完全删除的情况即可

注意:

  1. 如果使用(multiset)存储当前剩余的数,一定要先从中删除最大的数,再去进行检测,避免如果二者值相等的情况下,会误认为结果仍可以成立
  2. 不使用(set)用桶进行存储查询也是同样的做法
技术图片
#include<bits/stdc++.h>
using namespace std;
/*===========================================*/
const int maxn = 2005;
const int M = 1e6 + 5;
int dat[maxn];
multiset<int, greater<int>> s;
vector<pair<int, int>> ans;
int n, T;

int main()
{
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= 2 * n; ++i)
            scanf("%d", &dat[i]);
        sort(dat + 1, dat + 2 * n + 1);
        for (int i = 1; i < 2 * n; ++i)
        {
            ans.clear();
            s.clear();
            int last = dat[2 * n];
            ans.push_back(pair<int, int>(dat[2 * n], dat[i]));
            for (int j = 1; j < 2 * n; ++j) if (j != i) s.insert(dat[j]);
            while (!s.empty())
            {
                multiset<int, greater<int>>::iterator t;
                int tmp = *s.begin();
                s.erase(s.begin());
                if ((t = s.find(last - tmp)) != s.end())
                {
                    ans.push_back(pair<int, int>(tmp, *t));
                    last = tmp;
                    s.erase(t);
                }
                else
                    break;
            }
            if (ans.size() == n)
                break;
        }
        if (ans.size() == n)
        {
            printf("YES\n%d\n", ans[0].first + ans[0].second);
            for (auto& i : ans)
                printf("%d %d\n", i.first, i.second);
        }
        else
            printf("NO\n");
    }
}
View Code

 

D

题意:

n个数,每次选相邻的两个数都减一,当第i堆减到0时,第i?1和第i+1堆也不算是相邻,问在删除前允许交换任意相邻数一次的前提下能不能把所有数归零。

题解:

易知消除顺序不重要。

①如果不交换就能达成。 a1<=a2<=a3……<=ai     an<=an-1<=an-2<=……   设li 是第i个在消除后剩下的值 li = ai - li-1

维护前缀后缀和

②必须交换的。 以ai为基础 1~i-1前缀   i+2~n后缀  再判断 li-1   ai  ai+1   si+2 是否满足

技术图片
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
ll a[maxn] , p[maxn] , s[maxn];
int main()
{
    ios::sync_with_stdio(false);
    int t; cin >> t;
    while (t--){
        int n ; cin >> n;
        for (int i = 1 ; i <= n ; i++) cin >> a[i];
        p[0] = 0;
        for (int i = 1 ; i <= n ; i++) {
            if (p[i - 1] == -1) p[i] = -1;
            else p[i] = max( -1ll , a[i] - p[i - 1] );
        }
        s[n + 1] = 0;
        for (int i = n ; i >= 1 ; i--) {
            if (s[i + 1] == -1) s[i] = -1;
            else s[i] = max( -1ll , a[i] - s[i + 1] );
        }
        bool ok = true;
        for (int i = 1 ; i < n ; i++) if (p[i] == -1) ok = false;
        if (p[n]) ok = false;
        if (ok) {
            cout << "YES" << endl;
            continue;
        }
        for (int i = 1 ; i < n && !ok; i++) {
          //  if (p[i - 1] == -1 || s[i + 2] == -1) continue;
            ll tmp[4] = {p[i - 1] , a[i + 1] , a[i] , s[i + 2]};
            for (int j = 1 ; j <= 3 ; j++) {
                if (tmp[j - 1] == -1) tmp[j] = -1;
                else tmp[j] = max (-1ll , tmp[j] - tmp[j - 1]);
            }
            ok |= (tmp[1] >= 0 && tmp[2] >= 0 && tmp[3] == 0);
        }
        cout << (ok ? "YES" : "NO") << endl;
    }
    return 0;
}
View Code

 

E

题目:
给出一个长度n,给出操作(i,j),p[j]=i?swap(i,j),但要付出(i?j)2的代价,现需要求出对应的长度为n的排列可能拥有的最大代价、排列本身以及中间的操作过程

解析:

  1. 可以分析得出,交换可以使得当前值与值对应的位置进行交换,为了使交换的位置距离尽可能原,可以朴素的想到构造出形如2,3,4,…,n,1的序列,这样(1,n)(2,n)(n?1,n)不断进行交换,但这样却不是最优的
  2. 那么可以考虑构造形如n?1,3,4,…,?n/2?,1,n,?n/2?+1,?n/2?+2,…,n?2,2的序列,这样(n?1,1)(n?2,1)…(?n/2?+1,1),(2,n)(3,n)…(?n/2?,n),(n,1)进行交换,这样不难得出所需要的次数是(n?1)2+2?((n?2)2+?+(n??n/2?)2),再判断是否?n/2?为奇数加上(n?1??n/2?)2
  3. 其实上述的构造方式相当于第一种朴素构造的变形,从两边开始找尽可能长的交换路径(即两端非端点的位置开始向内部进行交换)

 

技术图片
#include<bits/stdc++.h>
#define rep(i,a,b) for(int  i=a;i<b;++i)
#define rep1(i,a,b) for(int  i=a;i<=b;++i)
#define rrep(i,a,b) for(int i=b-1;i>=a;--i)
#define rrep1(i,a,b) for(int i=b;i>=a;--i)
typedef long long LL;
using namespace std;
/*===========================================*/

const int maxn = 1e5 + 5;
LL sum[maxn];

int main() {
    rep(i, 1, maxn)
        sum[i] = sum[i - 1] + 1LL * i * i;
    int T;
    scanf("%d", &T);
    while (T--) {
        long long n;
        scanf("%lld", &n);
        long long t = n / 2;
        if (n == 2)
            printf("1\n2 1\n1\n2 1\n");
        else if (n == 3)
            printf("5\n2 3 1\n2\n2 1\n1 3\n");
        else
        {
            printf("%lld\n", (n - 1) * (n - 1) + 2 * (sum[n - 2] - sum[n - 1 - t]) + (n & 1 ? (n - 1 - t) * (n - 1 - t) : 0));
            printf("%lld", n - 1);
            rep(i, 2, t)
                printf(" %d", i + 1);
            printf(" 1 %lld", n);
            rep(i, t + 2, n)
                printf(" %d", i - 1);
            printf(" 2\n");
            printf("%lld\n", n - 1);
            rep1(i, 2, t)
                printf("%d %lld\n", i, n);
            rrep(i, t + 1, n)
                printf("%d 1\n", i);
            printf("%lld 1\n", n);
        }
    }
}
View Code

 

 

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!