Ozon Tech Challenge 2020(D思维,E构造 F 最少操作使gcd>1 玄学随机)

题目链接

D. Kuroni and the Celebration

题意:交互题,输入n个节点的树   每次你可以询问两个点,系统回复你这两个点的LCA,你现在最多询问n/2 次  求出这颗树的根节点。

做法:思路应该比较简单,就是代码难写,代码参考来自jiufeng  自己的码力优化还是很差

#include<bits/stdc++.h>
#define LL long long
#define PB push_back
#define POP pop_back()
#define PII pair<int,int>
#define FI first
#define SE second
#define ULL unsigned long long
using namespace std;
const int INF=0x3f3f3f3f;
const double pi=acos(-1),eps=1e-8;
const int N=1e3+10,M=N<<2;
int n,m;
int d[N];
int a[N][N];
bool vis[N];
void dfs(int x,int t){
    if(vis[x]==1||x==t)return;
    vis[x]=1;
    //cout<<x<<endl;
    for(int i=1;i<=n;i++){
        if(a[x][i]&&vis[i]==0){
            a[x][i]--;
            a[i][x]--;
            d[i]--,d[x]--;
            dfs(i,t);
        }
    }
}
void solve(int x,int y){
    printf("? %d %d\n",x,y);
    fflush(stdout);
    int anc;
    scanf("%d",&anc);
    dfs(x,anc);
    dfs(y,anc);
}
int main()
{
    //fflush(stdout);
    cin>>n;
    for(int i=1,u,v;i<n;i++){
        scanf("%d%d",&u,&v);
        a[u][v]=1;
        a[v][u]=1;
        d[u]++;
        d[v]++;
    }
    for(int i=1;i<=n/2;i++){
        int st=0,ed=0;
        for(int j=1;j<=n;j++){
            if(d[j]==1){
                if(st)ed=j;
                else st=j;
                if(ed)break;
            }
        }
        if(ed)solve(st,ed);
    }
    for(int i=1;i<=n;i++){
        if(vis[i]==0){
            printf("! %d\n",i);
        }
    }
    return 0;
}

E. Kuroni and the Score Distribution

题意:给n和m,输出n个数,这些数里必须要有m对a[i]+a[j]==a[k]  ( i < j < k )

做法:这题看了一脸懵,没点思路,搜题解:链接

最大对数的构造方法是,每个ai =i 的下标就是他的值一定是构造的最大的。因为我们可以证明出每个二元组得到的ak
​   
 一定是两两不同的。所以我们在n以内的对数就是0/2+1/2+3/2...+(n−1)/2 0/2+1/2+3/2...+(n-1)/20/2+1/2+3/2...+(n−1)/2因为要把自己给扣掉。然后我们如果这样构造的方案都小于m那么答案就是-1,否则我们需要让res=m就从后往前减去。(将某个a[i]+个值,破坏他能成对的对数)

最后我们不满足if条件的时候我们的res的值一定是落在m到m+(i-1)/2的范围之内的。这样答案还是多了。但是又不能直接减去(i-1)/2,这样减去太多了。

所以我们直接让a[i] 加上(res-m)*2这样就相当于减去了多余的对数(res-m)个。

(a[i]每加2,就会少一对满足条件的,)


其实当m=0的时候直接构造一连串连续的奇数就行了。

瞧瞧多么优秀的做法,我怎么就想不到呢?

有点类似dp方程构造一个最大值,现要求 最终dp方程是某个值,那么从后往前 遍历 干扰最终的dp方程,妙!

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e3+7;
int a[N],res[N];

signed main()
{
    int res=0;
    int n,m; scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++) a[i]=i;
    for(int i=1;i<=n;i++) res+=(i-1)/2;
    if(res<m) {
        cout<<-1<<endl;
        return 0;
    }
    //printf("res:%d\n",res);
    for(int i=n;i>=1;i--){
        if(res-(i-1)/2>=m){
            res-=(i-1)/2;
            a[i]=5e8+i*10000;
            continue;
        }
        a[i]+=(res-m)*2;
        break;
    }
    for(int i=1;i<=n;i++) printf("%lld ",a[i]);
    cout<<endl;
}
/*
7 8
*/

F. Kuroni and the Punishment

题意:有n个数,每次操作可以使某个数加一或者减一,使gcd>1的最少操作数量

题解参考来自此

做法:

很神奇的做法,妙不可言

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int n;
ll a[N],ans;

int random(int n){
    return (ll)rand()*rand()%n;
}

void check(ll x){
    ll res=0;
    for(int i=1;i<=n;i++)
        if(a[i]>=x) res+=min(a[i]%x,x-a[i]%x);
        else res+=x-a[i];
    ans=min(ans,res);
}

int main(){
    srand(time(0));
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    ans=n;
    for(int i=1;i<=50;i++){
        ll temp=a[random(n)+1];
        for(int k=-1;k<=1;k++){
            ll num=temp+k;
            if(num<1) continue;
            for(ll j=2;j*j<=num;j++){
                if(num%j!=0) continue;
                check(j);
                while(num%j==0) num/=j;
            }
            if(num>1) check(num);
        }
    }
    cout<<ans<<endl;
    return 0;
}
发布了498 篇原创文章 · 获赞 66 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/104656889