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

题目链接

D. Kuroni and the Celebration

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

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

#include
#define LL long long
#define PB push_back
#define POP pop_back()
#define PII pair
#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<>n;
    for(int i=1,u,v;i

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 
#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=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<

F. Kuroni and the Punishment

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

题解参考来自此

做法:

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

很神奇的做法,妙不可言

#include
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<

 

你可能感兴趣的:(codeforce题解)