Codeforces Round #592 (Div. 2) (19/11/4)

C - The Football Season

给定 n 次比赛,赢一局 获得 w 分,平一局获得 d 分,共获得 p 分,问已知可能的赢,平,输的局数。 其中 1<=n<=1e12,0<=p<=1e17,1<=d,w<=1e5。无解输出-1。

显然可以列出两个式子(其实题目中给了),设赢 x 局,平 y 局,输 z 局,则 x + y +z =n ,wx + dy = p。 首先斐蜀定理判断是否有解,然后显然是exgcd,求出通解,然后求出 x 或者 y 的一个最小正整数解,根据方程解出另一个看是否符合 大于等于 0 且 x+y<=n的条件。至于为什么是 x ,y其中一个取最小正整数解的时候求出另一个最优,因为通解的公式是 x2=x0p/gcd(w,d) + k d/gcd(w,d),y2 = y0p/gcd(w,d) - kw/gcd(w,d)。化简一下相当于 x2=x1 + kdelt_x,y2 = y1 - kdelt_y,显然delt_x,delt_y 中有一个较小的量,那么相当于将 x1 或者 y1全都转化为另一个,一定是 delt_ 小的那个比较优,x2 +y2 操作一次会整体变化 delt_小 - delt_大。

这个题目的数据比较大,注意有些量先模再乘,先除再乘。
也是比较少写这个算法,算是贴个板子。

#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define maxm 1006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define mod 10007
#define auto(i,x) for(int i=head[x];i;i=ed[i].nxt)
ll read(){
    ll x=0,f=1ll;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
ll n,p,w,d;
ll ex_gcd(ll a,ll b,ll &x,ll &y){
    if(a==0&&b==0)return -1;
    if(b==0){x=1;y=0;return a; }
    ll dd=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return dd;
}
ll gcd(ll x,ll y){return x==0?y:gcd(y%x,x); }
int main()
{
    n=read();p=read();w=read();d=read();
    ll gg=gcd(w,d);
    if(p%gg==0){
        ll x,y;
        ll dd=ex_gcd(w,d,x,y);
        //ll x1=p/gg*x;
        //ll y1=p/gg*y;
        ll p1=d/gg,p2=w/gg;
        //printf("%lld  %lld\n",x1,y1);
        ll x2=((p/gg)%p1*(x%p1)%p1 + p1)%p1;
        //y1=(y1%p2 + p2)%p2;
        ll y2=(p - w*x2)/d;
        //printf(" x2 = %lld   y2 = %lld\n",x2,y2);
        if(x2+y2<=n&&x2>=0&&y2>=0)printf("%lld %lld %lld\n",x2,y2,n-x2-y2);
        else {
            y2=((p/gg)%p2*(y%p2)%p2 + p2)%p2;
            x2=(p - d*y2)/w;
            //printf("--------x2 = %lld   y2 = %lld\n",x2,y2);
            if(x2+y2<=n&&x2>=0&&y2>=0)printf("%lld %lld %lld\n",x2,y2,n-x2-y2);
            else printf("-1\n");
        }
    }
    else printf("-1\n");
    return 0;
}
D - Paint the Tree

给一颗 n 个节点是树,现要求将每个节点染色,目前有三种颜色,每个节点染成某种颜色的代价已知,现要求一种染色方案,使得树上所有的长度为3的路径上所有的点颜色都各不相同,且代价最小。3<=n<=1e5。如果不存在这样的染色方案输出-1。

首先如果一个节点的出度是3,首先当前点需要选择一种颜色,剩下与之相邻的3个点只有两种颜色可选,那么根据抽屉原理,一定存在两个点颜色相同,所以长度为3的路径就会不满足有3个不同的颜色。所以这个树只能是一条链。然后找一个叶子节点,枚举开头的两个点的颜色,后面所有点的颜色就固定了,搜一遍即可。

#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define maxm 1006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define mod 10007
#define auto(i,x) for(int i=head[x];i;i=ed[i].nxt)
ll read(){
    ll x=0,f=1ll;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
int n,a[maxn][3];
struct edge{int v,nxt;}ed[maxn<<1];
int head[maxn],tot;
void add(int x,int y){ed[++tot]={y,head[x]};head[x]=tot; }
void ed_clr(int n){tot=0;inc(i,0,n)head[i]=0; }
int du[maxn],pre,pree,cc[maxn],tmp[maxn];
ll res;
void dfs(int x,int fa){
    if(x==0)return ;
    int col=4;
    inc(i,0,2)if(i!=pre&&i!=pree){col=i;break; }
    res+=a[x][col];
    pree=pre;pre=col;tmp[x]=col;
    for(int i=head[x];i;i=ed[i].nxt){
        int v=ed[i].v;
        if(v==fa)continue;
        dfs(v,x);
    }
}
int main()
{
    n=read();
    inc(i,0,2)inc(j,1,n)a[j][i]=read();
    int x,y;
    inc(i,1,n-1){
        x=read();y=read();
        add(x,y);add(y,x);
        du[x]++;du[y]++;
    }
    int ye=0;
    inc(i,1,n){
        if(du[i]>2){printf("-1\n");return 0;}
        if(du[i]==1)ye=i;
    }
    ll ans=inf;
    inc(i,0,2)inc(j,0,2){
        if(i==j)continue;
        pree=i,pre=j;
        //printf("%d %d\n",pree,pre);
        int st1=ye,st2=ed[head[st1]].v,st3;
        for(int k=head[st2];k;k=ed[k].nxt){
            if(ed[k].v!=st1){
                st3=ed[k].v;
                break;
            }
        }
        //printf("----- %d %d %d\n",st1,st2,st3);
        tmp[st1]=i;tmp[st2]=j;
        res=a[st1][i]+a[st2][j];
        dfs(st3,st2);
        if(ans>res){
            ans=res;
            inc(k,1,n)cc[k]=tmp[k];
        }
    }
    printf("%lld\n",ans);
    inc(i,1,n)printf("%d%c",cc[i]+1,i==n?'\n':' ');
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值