Ozon Tech Challenge 2020 (Div.1 + Div.2)

A

签到

B

给你一串有括号组成的字符串,要求你每次删去一个简单括号序列,要求你用最少的次数把字符串里的括号删完(简单括号序列:前n个为‘(’,后n个为’)‘);

思路:直接贪心字符串里的第一个括号(匹配字符串里的最后一个括号),这样匹配第一次肯定是删掉最长的,同时他删一次后就找不到其他的了

#include <bits/stdc++.h>
using namespace std;
const int maxn=4e5+10;
typedef long long ll;
int n,m,T,sum,piuy;
int cnt,resss,cntt;
string SSSS,ss;
int ans[maxn],b[maxn],kk[maxn],laa[maxn],poo[maxn];
bool kop(int a,int b){
    return a>b;
}
int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
    return x*f;
}
void ini()
{
    int pp=0;
    int ppp=0;
    for(int i=1;i<=10;i++)pp++;
    for(int i=10;i<=15;i++) ppp++;
}
void run()
{
    for(int i=0;i<SSSS.size();i++) {
        if(SSSS[i]==')') poo[++cnt]=i+1;
        else laa[++resss]=i+1;
    }
    sort(poo+1,poo+1+cnt,kop);
    sort(laa+1,laa+1+resss);
    int i=1,j=1;
    while(1){
        if(j>resss||cnt<i) break;
        if(laa[i]<poo[j]) {
            kk[++cntt]=laa[i];
            kk[++cntt]=poo[j];
            i++,j++;
            piuy=piuy+2;
        }
        else break;
    }
 
    if(piuy==0)
        puts("0");
    else{
    puts("1");
    cout<<piuy<<endl;
    sort(kk+1,kk+1+cntt);
    for(int i=1;i<=cntt;i++) cout<<kk[i]<<" ";
    puts("");
    }
 
}
int main()
{
    ini();
    cin>>SSSS;
    run();
}

  C

由抽屉原理可知,当n>mn>m时,必定存在两个aiai模mm同余,此时答案为00

否则可以直接On2暴力

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+11;
typedef long long ll;
int cnt,resss,cntt;
string SSSS,ss;
int ans[maxn],b[maxn],kk[maxn],laa[maxn],poo[maxn];
ll a[maxn],mod,n;
int mpp[maxn];
bool kop(int a,int b){
    return a>b;
}
ll read(){
    char c=getchar();ll x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
    return x*f;
}
void ini()
{
    int pp=0;
    int ppp=0;
    for(int i=1;i<=10;i++)pp++;
    for(int i=10;i<=15;i++) ppp++;
}
int run()
{
    n=read(),mod=read();
//	rep(i,1,n) a[i]=i;//scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++){
        if(mpp[a[i]%mod]){
            puts("0");
            return 0;
        }
        ++mpp[a[i]%mod];
	}
	ll ress=1;
	for(int i=1;i<=n;++i){
        for(int j=1+i;j<=n;j++){
            ress=ress*(a[i]-a[j])%mod;
        }
	}
    if(ress<=-1) ress=-ress;
	cout<<ress<<endl;
 
}
int main()
{
    ini();
    run();
}

  D

给你一棵树,求出他的根,你可以询问u,v的lca最多n/2次

可以发现一棵树度数为1的点至少会有一个,因此可以每次挑出两个度数为1的点x,yx,y进行询问,若结果等于其中一个点,则其必定为根。否则删除这两个点并把新出现的度数为1的点加入集合,最坏情况就是恰好询问n2⌊n2⌋次

#include <bits/stdc++.h>
using namespace std;
#define TLE std::ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define ll long long
#define pb push_back
typedef pair<int,int> pii;
const ll mod = 1000000000 ;
const int INF=0x3f3f3f3f;
using namespace std;
const int mxn = 3e5+7 ;
int n,t,m,k,l,r,res,prime[mxn],isprime[mxn],phi[mxn],dis[mxn],head[mxn];
string str;
vector<int>ans[mxn];
queue<int>q;
int cnt[mxn];
bool vis[mxn];
void del(int x)
{
    for(auto i : ans[x])
    {
        cnt[i]--;
        if(cnt[i]==1) q.push(i);
    }
}
int main()
{
    TLE;
    scanf("%d",&n);
    for(int i=1,u,v;i<n;i++)
    {
        scanf("%d %d",&u,&v);
        ans[u].pb(v);ans[v].pb(u);
        cnt[u]++;cnt[v]++;
    }
    for(int i=1;i<=n;i++)
        if(cnt[i]==1) q.push(i);
    while(q.size()>=2)
    {
        int u = q.front() ;q.pop();
        int v = q.front() ;q.pop();
        vis[u] = vis[v] = 1 ;
        printf("? %d %d
",u,v);
        fflush(stdout);
        cin>>k;
        if(k==u || k==v ) {printf("! %d
",k); return 0;}
        del(u);del(v);
    }
    k=0;
    for(int i=1;i<=n;i++)
        if(!vis[i])
            printf("! %d
",i) , k++;
//    assert(k==1);
    return 0 ;
}

  E

构造的话直接贪心构造。
123456...,最后补差就行。
因为从3开始贡献依次为1,1,2,2,3,3,4,4,
当最后一个的贡献大于我们需要的,也就是我们必须弄出恰好m种,而你继续加的话贡献又会大于m,不加又小于

我们发现对这个元素x加1,他的贡献就会减一,所以不段++直到满足,后面的直接从1e9补回来

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '
'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5000 + 5;
 
int n, m;
 
void run() {
    cin >> n >> m;
    if(n == 1) {
        if(m == 0) cout << 1 << '
';
        else cout << -1 << '
';   
        return;
    }
    vector <int> a;
    a.push_back(1);
    a.push_back(2);
    int now = 1, cnt = 0;
    int t = 0, p = 3;
    while(t < m) {
        if(t + now > m) {
            int r = m - t;
            --p;
            for(int i = p + 1;; i++) {
                int L = i - p, R = p;
                if((R - L + 1) / 2 == r) {
                    a.push_back(i);
                    t += r;
                    break;
                }
            }
        } else {
            a.push_back(p++);
            t += now;
            if(++cnt == 2) {
                ++now, cnt = 0;   
            }
        }
    }
    if(sz(a) > n) {
        cout << -1 << '
';
        return;   
    }
    int s = 1e9;
    while(sz(a) < n) {
        a.push_back(s);
        s -= 50000;   
    }
    sort(all(a));
    for(auto it : a) cout << it << ' ';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

  F

你有n个数, 你可以进行让某个数加一或减一的操作, 求最少的操作次数让这些数最大公因数不为1

思路,我们先假设gcd最少为2时候,那么最多操作只有n次对吧,因为gcd=2说明全是偶数,序列全是奇数我们只需每个位置操作1次,总共就n次。

在最多操作只有n次的条件下,那么一个位置操作数>=2的数量就是<=n/2,反过来说一个位置操作数小于等于1的位置个数就是大于等于n/2,那么也就是我们随机选择一个位置他操作个数小于等于1的几率起码是1/2,那么随机抽取20次,抽到操作个数大于等于2的就得是1/(2^20),忽略不计,所以枚举20次必定找到一个操作数小于等于1的

,那么找到这个数字后对他操作就是三个,-1,0,+1,三种情况分别判断一下,看哪个操作是最好次数就行

然后就行对这个数字的质因数进行判断看哪个质因数是最优的,因为所有数字的gcd,就是他们公共质因数

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#define ll long long
using namespace std;

template <typename T>
void read(T &x) {
    x = 0; bool f = 0;
    char c = getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
    for (;isdigit(c);c=getchar()) x=x*10+(c^48);
    if (f) x=-x;
}

template <typename T>
void write(T x) {
    if (x < 0) putchar('-'), x = -x;
    if (x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 205000;
ll a[N], ans, n;
map<ll, int> mp;
void check(ll k) {
    ll res = 0; if (mp[k]) return; mp[k] = 1;
    for (int i = 1;i <= n; i++) {
        if (a[i] >= k) res += min(a[i] % k, k - a[i] % k);
        else res += k - a[i];
        if (res >= ans) return;
    }
    ans = min(ans, res);
}

void work(ll x) {
    for (ll i = 2;i * i <= x; i++) {
        if (x % i) continue;
        while (x % i == 0) x /= i;
        check(i);
    }
    if (x > 1) check(x);
}

#include <cstdlib>
#include <ctime>
int main() {
//    freopen ("hs.in","r",stdin);
//  srand(time(0));
    read(n); ans = n;
    for (int i = 1;i <= n; i++) read(a[i]);
    random_shuffle(a + 1, a + n + 1);
    int lim = min(n, 300ll);
    for (int i = 1;i <= lim; i++) {
        ll x = a[i];
        work(x), work(x + 1); if (x > 1) work(x - 1);
    }
    cout << ans << endl;
    return 0;
}

  

原文地址:https://www.cnblogs.com/hgangang/p/12411709.html