2020牛客多校第九场补题博客
The Crime-solving Plan of Groundhog(I)
题目链接: The Crime-solving Plan of Groundhog
输入:
2
5
1 3 2 1 2
3
1 1 0
输出:
1223
10
题目大意: 给你n个数字(0~9)你需要把这些数字拼凑成两个不带前导0的数字,然后使得这两个数字的乘积最小,并输出乘积。
解题思路: 从这n个数字中找出最小的非0的那一个,当做其中一个数字,剩下的非零的数字按小到大的顺序拼凑形成第二个数字,最后将所有的0都插入到第二个数字的第二位开始即可。(举几个例子即可,不需要详细的证明 ^ - ^ )还需要注意的是,因为n很大,也就是数字的位数很多,需要用到高精度。(具体细节看代码)
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<string>
#include<sstream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
typedef long long ll;
using namespace std;
const ll mod = 998244353;
void _print(__int128 x)
{
if (x>=10) _print(x/10);
putchar(x%10+'0');
}
void print(__int128 x)
{
if (x<0) putchar('-'),x=-x;
_print(x);
}
vector<int> mul(vector<int> &A, int B)
{
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || t; i++)
{
if (i < A.size())
t += A[i] * B;
C.push_back(t % 10);
t /= 10;
}
reverse(C.begin(), C.end());
return C;
}
vector<int > V;
char num[100010];
int Num[100005];
int main()
{
int t,n;
cin>>t;
while(t--)
{
V.clear();
ans.clear();
int a,x;
int ind=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&Num[i]);
}
sort(Num,Num+n);
for(int i=0;i<n;i++)
{
if(Num[i]!=0)
{
ind=i;
break;
}
}
x=Num[ind];
num[0]=Num[ind+1]+'0';
for(int i=1;i<=ind;i++)
{
num[i]='0';
}
for(int i=ind+2;i<n;i++)
{
num[i-1]=Num[i]+'0';
}
num[n-1]=0;
int sum,J,Y;
sum=J=Y=0;
for(int i=n-2;i>=0;i--)
{
sum=(num[i]-'0')*x+J;
J=sum/10;
Y=sum%10;
V.push_back(Y);
if(i==0&&J!=0)
V.push_back(J);
}
int len=V.size();
for(int i=len-1;i>=0;i--)
printf("%d",V[i]);
cout<<endl;
}
return 0;
}
Groundhog Looking Dowdy(F)
题目链接: Groundhog Looking Dowdy
输入:
4 3
1 3
2 8 6
1 2
3 1 7 5
输出:
2
题目大意: 又一个人规划好了接下来n天可以穿的衣服,每件衣服都有一个价值,每天只能穿一件衣服。他现在想知道在m天内如何穿,可以使得所穿衣服的最大价值和最小价值的差的最小,并求出差的最小值。
解题思路: 由于我们需要找的是最大值和最小值的差的最小值,那么我们就可以对每件衣服 按照价值进行排序,然后我们就需要找的这样一个区间,这个区间里面的衣服够穿m天,并且区间内最大值和最小值的差是最小值。那么我们首先考虑满足够穿m天这个条件的区间,然后对于这些区间求一个最小值即可。那尺取法就刚好可以解决这个问题。需要注意的是我们可以对每件衣服设置两个属性:一个是价值,一个是所在的天数,天数是为了方便求区间内的衣服是否够穿m天。(具体细节看代码)
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<string>
#include<sstream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
typedef long long ll;
using namespace std;
const ll mod = 998244353;
void _print(__int128 x)
{
if (x>=10) _print(x/10);
putchar(x%10+'0');
}
void print(__int128 x)
{
if (x<0) putchar('-'),x=-x;
_print(x);
}
vector<int> mul(vector<int> &A, int B)
{
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || t; i++)
{
if (i < A.size())
t += A[i] * B;
C.push_back(t % 10);
t /= 10;
}
reverse(C.begin(), C.end());
return C;
}
struct str{
int v,pos;
}num[2000005];
int flag[1000005]; //动态的记录每个区间内每天有多少件衣服
bool cmp(str a,str b)
{
if(a.v==b.v)
return a.pos<b.pos;
return a.v<b.v;
}
int main()
{
int n,m;
int tmp,a,count=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&tmp);
for(int j=0;j<tmp;j++)
{
scanf("%d",&a);
num[count].v=a;
num[count++].pos=i;
}
}
sort(num,num+count,cmp);
int s,t,sum;
int ans=1000000009;
s=t=sum=0;
int out=0;
while(true)
{
out=0;
while(t<count&&sum<m)
{
out=1;
if(flag[num[t].pos]==0) //如果该天衣服数为0,那么加入进来后 衣服可穿的天数加一
{
sum++;
}
flag[num[t].pos]++;
t++;
}
while(sum>=m)
{
out=1;
ans=min(ans,num[t-1].v-num[s].v);
if(flag[num[s].pos]==1) //如果该天衣服数为1,那么删除之后后 衣服可穿的天数减一
sum--;
flag[num[s].pos]--;
s++;
}
if(out==0)
break;
}
cout<<ans<<endl;
return 0;
}
Groundhog Chasing Death(E)
题目链接: Groundhog Chasing Death
样例一:
输入:
1 2 1 2 8 4
输出:
2048
样例二:
输入:
1 2 3 4 120 180
输出:
235140177
题目大意: 给定x,y的值以及i,j的范围,求这样一个式子的值:
∏
i
=
a
b
∏
j
=
c
d
g
c
d
(
x
i
,
y
j
)
%
m
o
d
\prod_{i=a}^{b}\prod_{j=c}^{d}gcd(x^i,y^j)\%mod
∏i=ab∏j=cdgcd(xi,yj)%mod。
解题思路: 首先我们思考暴力的解法,过程是一个二重循环,因为数量级在106因此会超时。那么我们思考两次相邻的gcd之间是否存在某种联系。例如gcd(x,y)和gcd(x,y2),我们发现gcd(x,y)其实就是x和y的相同质因数的乘积,那y2的质因数只是在数量上是y的质因数的一倍,那么我们把x的质因数整理为数字以及对应的个数(例如:8有质因数2,2的个数为3;6有质因数:2,3, 分别有1,1个)那么我们就可以用这个思路来解决这道题。首先我们分别求出x,y的质因数,以及每个质因数的个数。(由于数量级为109,所以质因数的种类不会超过20个,每种质因数的个数也不会很多),然后找出x和y的共同质因数。并且开始遍历每个质因数,这里我们接着控制x的指数(也就是控制x中每个质因数的个数),变化y的指数。我们知道y中的每个质因数的个数变化是等差数列,因此我们可以在O(1)的时间求出c~d的指数范围内,该质因数的个数之和,即为z。然后我们计算好ans=ans*az(a表示当前这个质因数),就处理好当前这个质因数了。这里我们要注意因为我们是将某个质因数的个数全部求和后才进行快速幂的,因此这个z是有可能超过long long 的,因此我们需要对它取模,幂的取模需要用到欧拉函数,也就是这样:az%mod=az%φ(mod)%mod。由于mod是一个质数因此φ(mod)=mod-1;(具体细节看代码)
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<string>
#include<sstream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
typedef long long ll;
using namespace std;
const ll mod = 998244353;
void _print(__int128 x)
{
if (x>=10) _print(x/10);
putchar(x%10+'0');
}
void print(__int128 x)
{
if (x<0) putchar('-'),x=-x;
_print(x);
}
int prime[5800005];
bool Is_prime[1000005];
int solve(int n)
{
int count=0;
memset(Is_prime,1,sizeof(Is_prime));
Is_prime[0]=false;
Is_prime[1]=false;
for(int i=2;i<=n;i++)
{
if(Is_prime[i])
{
prime[++count]=i;
}
for(int j=1;j <=count && i*prime[j] <= n;j++)
{
Is_prime[prime[j]*i]=false;
if(i%prime[j]==0)break;
}
}
return count;
}
struct str{
ll v,cnt;
};
vector<str>p;
vector<str>q;
map<int,int> mp_a;
map<int,int> mp_b;
ll kuai(ll a,ll b)
{
ll ans=1;
while(b>0)
{
if(b&1)
ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
int main()
{
ll a,b,c,d,x,y;
cin>>a>>b>>c>>d>>x>>y;
int count=0;
count=solve(100000); //求出10^5以内的质数
for(int i=1;i<=count;i++) //求出x的质因数,及其个数
{
if(x%prime[i]==0)
{
x/=prime[i];
mp_a[prime[i]]++;
i--;
}
}
if(x!=1)//判断最后一个质因数是否大于10^5
mp_a[x]++;
for(int i=1;i<=count;i++) //y同理
{
if(y%prime[i]==0)
{
y/=prime[i];
mp_b[prime[i]]++;
i--;
}
}
if(y!=1)
mp_b[y]++;
map<int,int>::iterator it_a,it_b;
for(it_a=mp_a.begin();it_a!=mp_a.end();it_a++) //求出x和y的公共质因数,及其个数
{
for(it_b=mp_b.begin();it_b!=mp_b.end();it_b++)
{
str tmpa,tmpb;
if(it_a->first==it_b->first)
{
tmpa.cnt=it_a->second;
tmpa.v=it_a->first;
tmpb.cnt=it_b->second;
tmpb.v=it_b->first;
p.push_back(tmpa); //p存放x和y公共的质因数及其在每个质因数在x中的个数
q.push_back(tmpb); //p存放x和y公共的质因数及其在每个质因数在y中的个数
break;
}
}
}
int len=q.size();
if(len==0)
{
printf("1\n");
return 0;
}
if(a==0)
a=1;
if(c==0)
c=1;
ll ans=1;
for(int i=0;i<len;i++) //遍历每个质因数
{
ll z=0;
ll tmpx,tmpy;
for(int j=a;j<=b;j++) //循环控制x的幂
{
tmpx=(p[i].cnt*j)%mod; //tmpx表示x^j 中该质因数的个数
tmpy=(q[i].cnt*c)%mod; //tmpx表示y^c 中该质因数的个数
if(tmpx<=tmpy) // tmpx 小于 y^c 中该质因数的个数
{
z+=tmpx*(d-c+1);
}
else if(tmpx>=(q[i].cnt*d)) //tmpx大于 y^d 中该质因数的个数
{
z+=(q[i].cnt*c+q[i].cnt*d)*(d-c+1)/2;
}
else //tmpx 在两者之间 即分段求取
{
ll r=tmpx/q[i].cnt;
z+=(d-r)*tmpx;
z+=(tmpy+(r)*q[i].cnt)*(r-c+1)/2;
}
z%=mod-1; //对幂取模
}
ans=(ans*kuai(q[i].v,z))%mod;
}
cout<<ans<<endl;
return 0;
}
The Flee Plan of Groundhog(K)
题目链接: The Flee Plan of Groundhog
输入:
7 2
1 2
2 5
5 7
5 6
3 6
3 4
输出:
1
芜湖~ 我回来了
题目大意: 存在n间宿舍,编号为1~n。主人公A(位于1号宿舍)想去看望他的好兄弟B。A的移动速度为1m/s,每个相邻宿舍之间的距离都是1m,A在行动t秒后,得知B感冒了,与此同时,B迫不及待的来找A,A立刻逃跑,B的移动速度是2m/s。问A在得知消息后最晚多少秒被B抓到。
解题思路: 很显然,这个题是一道树的题。那么我们就要找到树的根。我们发现以n节点为根是合适的,我们假设A在得知消息的时候,位于x节点,那么A有两种移动方向,一种是往子树方向跑,另一种是先往父节点跑,再往子树跑。当x节点的深度大于等于n节点到x节点的距离,那么只需要往子树跑,并且时间就是n到x的距离。反之我们就需要先算出位于x节点的抓捕时间,然后A跑到x的父节点,再次计算时间,然后与位于x节点的抓捕时间进行比较,取较大值。也就是说先计算位于x节点的时间,如果不等于n到x的距离(如果等于说明在x节点上可以取到最大值),那么就花一秒时间向上到父节点,然后对于父节点的处理和之前对x节点的处理是一样的。(这里要注意:向上移动到父节点,需要花费一秒的时间,移动后A与B之间的距离会减少3m)那么我们要得到哪些信息呢,首先是每个节点的深度,以及n节点到每一个节点的距离。(具体细节看代码)
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<string>
#include<sstream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
typedef long long ll;
using namespace std;
const ll mod = 998244353;
const int N=1e5+5;
void _print(__int128 x)
{
if (x>=10) _print(x/10);
putchar(x%10+'0');
}
void print(__int128 x)
{
if (x<0) putchar('-'),x=-x;
_print(x);
}
int d[N];
int pre[N];
int flag[N];
int T[N];
vector<int > E[N];
int dfs_fa(int x,int fa)
{
int cnt=0;
pre[x]=fa;
flag[x]=1;
for(int i=0;i<E[x].size();i++)
{
if(flag[E[x][i]]==0)
{
T[E[x][i]]=T[x]+1; //计算出 n节点到每个节点的距离
d[x]=max(d[x],dfs_fa(E[x][i],x)+1); //求出深度
cnt++;
}
}
if(cnt==0) //叶子节点 深度置为1
d[x]=0;
return d[x];
}
int main()
{
int n,t;
cin>>n>>t;
for(int i=0;i<n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
// a=i+1;
// b=a+1;
E[a].push_back(b);
E[b].push_back(a);
}
int root=n;
dfs_fa(root,-1);
int x=1;
// while(x!=-1)
// {
// cout<<x<<"->"<<pre[x]<<':'<<d[x]<<endl;;
// x=pre[x];
// }
// x=1;
for(int i=0;i<t;i++) //求出t秒后A 所在的位置
x=pre[x];
int ans;
int cnt=0,tmp=x;
cnt=T[x]; //最开始A,B之间的距离记为cnt
if(cnt==0)
{
cout<<0<<endl;
return 0;
}
// cout<<cnt<<endl;
tmp=-1; //tmp用来记录向上走了多少个父节点
while(cnt>=1)
{
tmp++;
if(d[x]>=cnt) //A所在位置的深度,大于A,B之间的距离,那么就不用向上走了 直接求出结果
{
ans=max(ans,cnt+tmp);
break;
}
else
{
ans=max(ans,((cnt+d[x])/2+((cnt+d[x])%2==1?1:0))+tmp);
//(cnt+d[x])/2+((cnt+d[x])%2==1?1:0)表示A在x位置,B多久追上A
}
x=pre[x]; //向上走到父节点
cnt-=3; //A,B之间的距离减3
}
cout<<ans<<endl;
return 0;
}