7.20 2020牛客暑期多校训练营(第四场)题解及补题
7.20 2020牛客暑期多校训练营(第四场)题解及补题
比赛过程
过2题,B和F,F题经过第一次,总结第二发过了;B题找蕴含的规律没找对,最后发现是质因子。
题解
B Basic Gcd Problem
题意
\(f_c(x)=max_{i=1 \cdots x-1}c×f_c(gcd(i,x)),x>1\)
\(f_c(1)=1,x=1\)
给你T组数据,每组输入\(n,c\),求\(f_c(n)\)的值。
解法
\(n\)的质因子有几个,\(c\)的幂就有几次。
代码
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(0), cin.tie(0)
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
const int inf = ~0u >> 1;
const ll mod = 1e9 + 7;
int prime[maxn];
int visit[maxn];
void Prime() {
for (int i = 2; i <= maxn; i++) {
if (!visit[i]) {
prime[++prime[0]] = i;
}
for (int j = 1; j <= prime[0] && i * prime[j] <= maxn; j++) {
visit[i * prime[j]] = 1;
if (i % prime[j] == 0) {
break;
}
}
}
}
int dec(int n) {
int ans = 0;
for (int i = 1; i <= prime[0] && prime[i] * prime[i] <= n; ++i) {
if (n % prime[i] == 0) {
while (n % prime[i] == 0) {
n /= prime[i];
ans++;
}
}
}
if (n > 1)
ans++;
return ans;
}
ll qpowmod(ll a, ll b, ll p) {
a %= p;
ll ans = 1, base = a;
while (b != 0) {
if (b & 1) ans = ans * base % p;
base = base * base % p;
b >>= 1;
}
return ans;
}
int main() {
IO;
Prime();
int t;
cin >> t;
while (t--) {
ll n, c;
cin >> n >> c;
cout << qpowmod(c, dec(n), mod) << endl;
}
return 0;
}
C Count New String
题意
给一个字符串,并且定义字符串函数\(f(S,x,y)(1<=x<=y<=n)\),返回一个长度为\(y-x+1\)的字符串,第\(i\)位是\(max_{i=x,x+1……x+k-1}S_i\)
设集合\(A={f(f(S,x1,y1),x2-x1+1,y2-x1+1)|(1<=x1<=x2<=y2<=y1<=n)},\)求集合\(A\)的大小
解法
大佬们用的序列自动机+广义后缀自动机,我们对后缀自动机还做不到很灵活的运用。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e6+100;
char s[N];
int tot,last,id[N],nx[N][10];
struct Node
{
int ch[10];
int fa,len;
}st[N<<1];
inline int newnode()
{
tot++;
for(int i=0;i<10;i++)
st[tot].ch[i]=0;
st[tot].fa=st[tot].len=0;
return tot;
}
void add(int x)
{
int p=last;
//
if(st[p].ch[x])
{
int q=st[p].ch[x];
if(st[q].len==st[p].len+1)
last=q;
else
{
int np=last=++tot;
st[np].len=st[p].len+1;
st[np].fa=st[q].fa;
st[q].fa=np;
for(int i=0;i<10;i++)
st[np].ch[i]=st[q].ch[i];
while(st[p].ch[x]==q)
st[p].ch[x]=np,p=st[p].fa;
}
return;
}
//
int np=last=++tot;
st[np].len=st[p].len+1;
while(p&&!st[p].ch[x])st[p].ch[x]=np,p=st[p].fa;
if(!p)st[np].fa=1;
else
{
int q=st[p].ch[x];
if(st[p].len+1==st[q].len)st[np].fa=q;
else
{
int nq=++tot;
st[nq]=st[q]; st[nq].len=st[p].len+1;
st[q].fa=st[np].fa=nq;
while(p&&st[p].ch[x]==q)st[p].ch[x]=nq,p=st[p].fa;//向上把所有q都替换成nq
}
}
}
void init()
{
last=1;
tot=0;
newnode();
}
int main()
{
init();
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=0;i<10;i++)
nx[n+1][i]=n+1;
for(int i=n;i>=1;i--)//nx[i][j]:第i个位置(包括)后首次出现大于等于j的位置
{
for(int j=0;j<10;j++)
nx[i][j]=nx[i+1][j];
nx[i][s[i]-'a']=i;
for(int j=8;j>=0;j--)
nx[i][j]=min(nx[i][j],nx[i][j+1]);
}
id[n+1]=last;
for(int i=n;i>=1;i--)
{
int pos=nx[i+1][s[i]-'a'];
last=id[pos];
for(int j=i;j<pos;j++)
add(s[i]-'a');
id[i]=last;
}
LL ans=0;
for(int i=1;i<=tot;i++)
ans+=st[i].len-st[st[i].fa].len;
printf("%lld\n",ans);
return 0;
}
F Finding the Order
题意
给出两条平行线\(l_1\)跟\(l_2\),\(l_1\)上有\(A\)、\(B\)两点,\(A\)在\(B\)的左边,\(l_2\)上有\(C\)、\(D\)两点但\(C\)、\(D\)两点的相对位置不知道,给出\(AC\) \(AD\) \(BC\) \(BD\)的长度,判断\(C\)在\(D\)的左还是右边
解法
方法一:
找出四条边最长的边,接的是A就是最右边的点,接的是B是最左边的点
方法二:
也就是我们比赛时想到的方法,首先假设\(|AB|=1\),然后求\(∠ABD\)和\(∠ABC\)的余弦,比较余弦大小值即可。
方法三:
小学数学知识,由于梯形的四边形对角线之和要大于两腰之和,
证明:
∵\(OA+OD>AD\),\(OB+OC>BC\)
∴\((OA+OC)+(OB+OD)>AD+BC\)
∴\(AC+BD>AD+BC\)
所以只需要判断一下\(b+cb+c\)是否\(>\),\(a+d>a+d\)即可。
代码
方法一:
#include<stdio.h>
#include<algorithm>
using namespace std;
int main()
{
int t,AC,AD,BC,BD;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d%d",&AC,&AD,&BC,&BD);
int maxn=max(AC,max(AD,max(BC,BD)));
if(maxn==AD||maxn==BC)
{
printf("AB//CD\n");
}
else //if(maxn==AC||maxn==BD)
{
printf("AB//DC\n");
}
}
return 0;
}
方法二:
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
double ans1,ans2;
ans1=(1.0+(double)d*(double)d-(double)b*(double)b)/(2.0*(double)d);
ans2=(1.0+(double)c*(double)c-(double)a*(double)a)/(2.0*(double)c);
if(ans1>ans2){//1<2
cout<<"AB//DC"<<endl;
}else{
cout<<"AB//CD"<<endl;
}
}
return 0;
}
方法三:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int main(){
int t;
cin>>t;
while(t--){
int a,b,c,d;
cin>>a>>b>>c>>d;
puts(b+c>a+d?"AB//CD":"AB//DC");
}
return 0;
}
H Harder Gcd Problem
题意
给出一个n,要求找出1-n这n个数中两个数gcd>1且不重复的最大对数,并打印出来。
一共T组数据,每组只有一个n
解法
因为是gcd>1很容易就能想到是每个搭配都一定是质数与其倍数或质数不同倍数之间的搭配。进而就可以得出
一个大于n/2的质数在n中一定不存在搭配(因为乘以2就大于n了),对于剩下不大于n/2的质数采取的贪心策略应该是:从大到小进行匹配,因为越大的质数,合适的匹配越少,如果质数从小到大匹配就可能出现小的质数匹配了大的质数的
倍数就会导致大的质数匹配减少而漏算,如果一个素数加上其倍数共有偶数个,则可以两两一组完全匹配,如果是奇数个
则先把该素数的最小倍数筛除,再把剩下的数两两匹配。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int v[maxn],prime[maxn],ans[maxn];
bool vis[maxn];
int m,n;
void getprime(int n){
memset(v,0,sizeof(v));
m=0;
for(int i=2;i<=n;i++) {
if(v[i]==0) {
v[i]=i;
prime[++m] = i;
}
for(int j=1;j<=m;j++) {
if(prime[j]>v[i]||prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
}
}
}
int main(){
getprime(2e5+100);
int t;
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
memset(ans,0,sizeof(ans));
memset(vis,0,sizeof(vis));
int x=0;
int pos=upper_bound(prime+1,prime+m+1,n/2)-prime-1;
for(int i=pos;i>0;i--) {
for(int j=prime[i];j<=n;j+=prime[i]) {
if(vis[j]||j==(prime[i]<<1))
continue;
ans[x++] = j;
vis[j]++;
}
if(x&1){
ans[x++]=(prime[i]<<1);
vis[ans[x-1]]++;
}
}
printf("%d\n",x/2);
for(int i=0;i<x;i+=2) {
cout<<ans[i]<<" "<<ans[i+1]<<endl;
}
}
}

浙公网安备 33010602011771号