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;
}