Codeforces Avito Cool Challenge 2018 A-E

(数学场吧=。=)

A题:https://codeforces.com/contest/1081/problem/A

题解 :

题意让我们将一个数进行减法操作,减数不能是被减数的因数,且可以进行多次这样的操作。

可以想到一点就是当n大于2时 n-1肯定不是他的因数 

所以只要对2进行特判即可

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=3e5+7;
const long long mod=1e9+7;

int main(){
    int n;
    scanf("%d",&n);
    if(n==2) printf("2\n");
    else printf("1\n");
    return 0;
}

B题:https://codeforces.com/contest/1081/problem/B

题解:

这题运用了分块的思想

首先题目给出与第i个人不同的有多少人 那么我们就可以求出与第i个人一样的有多少。

然后将得出的新数列进行排序,得出几段连续的的块

在值相同的块中,看其能不能分成与其值等值的分块

例如4 4 4 4 4 4 4 4 可以分为2块4 4 4 4块

而4 4 4 4 4 4 4 只能分为1块4 4 4 4块和1块4 4 4块 显然3个4组成的块是构成不了的

因此这题就可以简单化了

分块加统计相同权值的个数看这些个数是否能被权值整除,最后判断块数是否小于帽子总数即可

(期间对相同人数小于等于0的特判掉即可)

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=3e5+7;
const long long mod=1e9+7;
struct node{
    int x,num,ans;
}b[N];
int a[N];
bool cmp(node a,node b){
    return a.num<b.num;
}
bool cmp1(node a,node b){
    return a.x<b.x;
}
int main(){
    int n,sum=0;
    scanf("%d",&n);
    bool mark=true;
    /*bool mark=true;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum+=(n-1-a[i]);
        if(n-1-a[i]<0) mark=false;
    }
    if(sum%2==1) mark=false;
    if(sum>(n-1)*n) mark=false;
    */
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i].num=n-a[i];
        b[i].x=i;
        if(b[i].num<=0) mark=false;
    }
    sort(b+1,b+1+n,cmp);
    b[0].num=-1;
    b[0].ans=0;
    int tmp=-1;
    for(int i=1;i<=n;i++){
        if(b[i].num==b[i-1].num){
            //cout<<tmp<<endl;
            if(tmp>=b[i].num){
                b[i].ans=b[i-1].ans+1;
                if(b[i].ans>n) mark=false;
                tmp=1;
            }
            else {
                b[i].ans=b[i-1].ans;
                tmp++;
            }
        }
        else {
            b[i].ans=b[i-1].ans+1;
            if(b[i-1].num!=tmp) mark=false;
            if(b[i].ans>n) mark=false;
            tmp=1;
        }
    }
    //cout<<tmp<<endl;
    if(tmp!=b[n].num) mark=false;
    if(!mark) puts("Impossible");
    else {
        puts("Possible");
        sort(b+1,b+1+n,cmp1);
        for(int i=1;i<=n;i++){
            printf("%d",b[i].ans);
            if(i==n) puts("");
            else printf(" ");
        }
    }
    return 0;
}

C题:https://codeforces.com/contest/1081/problem/C

题解:

给我们1*n的块,m种颜色,并告诉我们可以将这1*n切k刀使得相邻块颜色不同。

小学奥数排列组合题

可以得出分块的种数是Cn-1取k

对于分完的块。进行染色 又可以得出对于每种切法染色的种数是m*(m-1)的k次

于是我们将这两步相乘即可

注意除法取膜求逆元

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <vector>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
const int N=3e5+7;
const long long mod=998244353;
LL ksm(LL a,LL b,LL MOD)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%MOD;
        a=(a*a)%MOD;
        b>>=1;
    }
    return ans;
}
LL inv2(LL a,LL MOD)//费马小定理
{
    return ksm(a,MOD-2,MOD);
}
long long cal(int x,int y){
    long long res=1;
    for(int i=1;i<=y;i++){
        res*=(x-i+1);
        res%=mod;
        res*=inv2(i,mod);
        res%=mod;
    }
    return res;
}
int main(){
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    long long ans=cal(n-1,k);
    //cout<<"---"<<ans<<endl;
    for(int i=1;i<=k;i++){
        ans*=(m-1);
        ans%=mod;
    }
    ans*=m;
    ans%=mod;
    printf("%lld\n",ans);
    return 0;
}

D题:

(下午csp第四题的进化版)

题解:

题目给出一张图(含自环和重边)并告诉你一些特殊点,让你求出这些特殊点到其他特殊点路径中的最大边的最大值

可以想到的是最小生成树prime算法的原理就是将当前树外到树最短的边连入树,这样不难发现当刚好最后一个特殊点连入树那么这条边就是其他点到该特殊点的最小路径最大边。且我们还能得出结论这个最大边就是最后所以点的答案。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=3e5+7,M=2e6;
const long long mod=1e9+7;
bool vis[N],VIS[N];
int head[N],ver[M],wei[M],NEXT[M],dis[N],ans,ti,k,tot,n,m,a[N];
void link(int u,int v,int w){
    ver[++tot]=v;
    NEXT[tot]=head[u];
    head[u]=tot;
    wei[tot]=w;
}
void prime(int x){
    memset(vis,false,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    priority_queue<pair<int,int> >q;
    q.push(make_pair(0,x));
    dis[x]=0;
    while(!q.empty()){
        int now=q.top().second;
        q.pop();
        if(vis[now]) continue;
        if(VIS[now]) ti++;
        ans=max(ans,dis[now]);
        if(ti==k) return;
        vis[now]=true;
        for(int i=head[now];i;i=NEXT[i]){
            int y=ver[i];
            if(vis[y]) continue;
            if(dis[y]>wei[i]){
                dis[y]=wei[i];
                q.push(make_pair(-dis[y],y));
            }
        }
    }
}
int main(){
    tot=0;
    int u,v,w;
    scanf("%d%d%d",&n,&m,&k);
    memset(head,0,sizeof(head));
    memset(VIS,false,sizeof(VIS));
    ans=0,ti=0;
    for(int i=1;i<=k;i++){
        scanf("%d",&a[i]);
        VIS[a[i]]=true;
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        link(u,v,w);
        link(v,u,w);
    }
    prime(a[1]);
    for(int i=1;i<=k;i++){
        printf("%d",ans);
        if(i==k) puts("");
        printf(" ");
    }
    return 0;
}

E题:https://codeforces.com/contest/1081/problem/E

题解:

已知偶数项求奇数项,并且满足数列的每一项的前缀和都是平方数。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=3e5+7;
const long long mod=1e9+7;
long long b[N],a[N];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n/2;i++){
        scanf("%lld",&a[i]);
    }
    long long sum=0;
    bool mark=true;
    for(int i=1;i<=n/2;i++){
        long long tmp=sqrt(sum+a[i])+1;
        while(1){
            long long x=tmp*tmp-sum-a[i];
            long long y=x+sum;
            long long pp=sqrt(y);
            if(pp>a[i]/2) {
                mark=false;
                break;
            }
            if(pp*pp==y){
                b[i]=x;
                sum+=x;
                sum+=a[i];
                break;
            }
            else tmp++;
        }
    }
    if(!mark) puts("No");
    else {
        puts("Yes");
        for(int i=1;i<=n/2;i++){
            printf("%lld %lld",b[i],a[i]);
            if(i==n/2) puts("");
            else printf(" ");
        }
    }
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值