A. Orac and Factors(规律)
思路
- 题意
- 定义 f(n)为n的大于1的第一个因子的值,先我们要进行k次操作,每次操作令 n += f(n), 问经过 k 次这样的操作之后 n 的值变成了多少
- 分析
- 如果 n 为偶数,f(n)的值恒为 2,直接计算
- 如果n 为奇数,f (n) 的值也为奇数,不过这个奇数的值需要我们自己算出来,这样第一次操作的时候
n += 奇数 = 偶数
,那么之后无论进行多少次操作 n恒为偶数,怎f(n)的值恒为2
代码
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<string>
#include<queue>
#include<map>
void fre() { freopen("A.txt","r",stdin); freopen("Ans.txt","w", stdout); }
#define ios ios::sync_with_stdio(false)
using namespace std;
#define ll long long
#define db double
#define INF 0x3f3f3f3f
const int mod =998244353;
const int mxn = 2e5 + 10;
int main()
{
/* fre(); */
/* ios; */
int T;
scanf("%d", &T);
while(T --)
{
int n, k;
scanf("%d %d", &n, &k);
for(int i = 2; i <= n; i ++)
{
if(n % i == 0)
{
n += i;
break;
}
}
if(k == 1)
{
printf("%d\n", n);
continue;
}
else
{
k --;
printf("%d\n", n + 2 * k);
}
}
return 0;
}
B. Orac and Models
思路
- 题意
- 给我们 n个数字组成的序列ar,现在让我们选择其中的一些数按在ar中下标从小到大排序,形成一组br,对于这个组内的任意相邻两元素要求br[i+1]在原来ar中的下标是br[i]在原原来ar中的下标的 倍数,且 br[i+1] > br[i]
- 分析
- 明显一道dp题,从后往前dp更容易解决问题
- 对于第i数字我们可以考虑选和不选,如果选择我们这个数字,就枚举 i的倍数作为转移过来的 “状态”,并选则其中最优的状态
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt", "w", stdout); }
using namespace std;
#define ll long long
const int mxn = 1e5 + 10;
int dp[mxn];
int ar[mxn];
int main()
{
/* fre(); */
int T;
scanf("%d", &T);
while(T --)
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
{
scanf("%d", &ar[i]);
dp[i] = 1;
}
for(int i = n/2; i >= 1; i --)
{
for(int j = i + i; j <= n; j += i)
if(ar[i] < ar[j])
dp[i] = max(dp[i], dp[j] + 1);
}
int ans = 0;
for(int i = 1; i <= n; i ++)
ans = max(ans, dp[i]);
printf("%d\n", ans);
}
return 0;
}
C. Orac and LCM
思路
- 题意
- 给我们一个有n个元素的序列s,s中所有任意两个元素的 LCM形成一个 集合,现在让我们求这个集合的 GCD。
- 分析
思路1:
- 我们设 序列s为
a,b,c
,现在让我们考虑a分别与b,c形成的Lcm之后被求gcd的结果: - 说明:
,首先如果我们把 a*gcd(b,c) gcd(a,b)与gcd(b,c)$之间没有共同的因子k的话,这个等式是成立的,否则的话相当于我们 扩大了k倍,因此我们要再除以k,而 , - 有了上面的推理我们,直接求一波后缀gcd,,,就能得到ans,剩下的具体看代码
思路2
5. 如果我们考虑一个k是s的n个元素的公因数(如果某个数是k的因数的话,可能包含的是k的为倍数) ,那么s中任意两个数的lcm都会包含因数k,(这里先考虑一下lcm一个简单的东西,设两个数 a,b分别有公因子
这个时候
,那么c一定有因子
),那么这个时候我们考虑s产生的所有lcm包含的公共因子是k的几次方,肯定是s中n个数中为k的倍数的因子中最小的那两个因子的设为
且x<=y, 那么所有为k的倍数的因子对答案的贡献是
,那么现在我就是要求:n个数中所有k的倍数因子中次小的那个因子的次方
6. 如果k是s中n-1个元素的公因数,那么任意两个数的lcm也都会包含k的次方,我们还考虑n个数中最小的两个k的倍数因子,会给答案作出贡献,而这个最小的两个k的倍数的因子分别为
, 那么它们对答案的贡献是
,而这个k^y,而
是s中n-1个数中包含k的最小的那个倍数因子
7. 如果 k在s中n个数因子中的因子数小 n-1,那么这种情况对答案的贡献恒为1
8. 那么问题就转化为对k数进行质因子分解,并维护某个因子的最小的次方、次小次方,之后在对所有因子出现的次数进行讨论,,,
代码(思路1)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt", "w", stdout); }
using namespace std;
#define ll long long
const int mxn = 1e5 + 10;
ll ar[mxn];
ll suf[mxn]; //后缀 gcd
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
int main()
{
/* fre(); */
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%lld", &ar[i]);
suf[n] = ar[n]; //注意要特殊考虑 n == 2 的时候的情况
suf[n - 1] = gcd(ar[n - 1], suf[n]);
ll res = ar[n - 1] * suf[n] / suf[n - 1];
for(int i = n - 2; i >= 1; i --) //这样也是因为也特殊考n == 2的时候的情况而从 i = n - 2的时候开始的
{
suf[i] = gcd(ar[i], suf[i + 1]);
res = gcd(res, ar[i] * suf[i + 1] / suf[i]);
}
printf("%lld\n", res);
return 0;
}
代码(思路2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=2e5+5;
const int INF =1e9;
ll a[MAXN],n;
ll visit[MAXN];
int prime[MAXN],cnt;int max_index;
bool notprime[MAXN];
void get_prime()
{
for(int i=2; i<=200000; i++)
{
if(!notprime[i])
prime[cnt++]=i;
for(int j=0; j<cnt&&prime[j]*i<=200000; j++)
{
notprime[prime[j]*i]=1;
if(i%prime[j]==0)
break;
}
}
}
vector<int>v[MAXN];
ll qp(ll a,ll i)
{
ll res = 1;
while(i)
{
if(i&1)
res*=a;
a*=a;
i>>=1;
}
return res;
}
void div(int x)
{
for(int i=0;i<cnt;i++)
{
if(prime[i]*prime[i]>x)
break;
if(x%prime[i]==0)
{
int num=0;max_index=max(max_index,i);
while(x%prime[i]==0)
{
x/=prime[i];num++;
}
v[prime[i]].push_back(num);
}
}
if(x>1)v[x].push_back(1),max_index=max(max_index,x);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
get_prime();
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i];
for(int i=1; i<=n; i++)
{
div(a[i]);
}
ll ans=1;
for(int i=0; i<=max_index; i++)
{
int s = v[prime[i]].size();
if(s)
{
sort(v[prime[i]].begin(),v[prime[i]].end());
if(s==n-1)
ans*=qp(prime[i],v[prime[i]][0]);
if(s==n)
ans*=qp(prime[i],v[prime[i]][1]);
}
}
cout<<ans<<endl;
return 0;
}
D. Orac and Medians
思路(规律 + 思维)
- 题意
- 给我们 n 个数的序列ar,我可以任选ar中的一个连续的区间 [l,r] 我们可以将这个区间内的所有数字替换成位于这个区间所有元素的中位数,先在给我们一个k,问我们经过任意 上述变换,能否是ar中所有元素都为 k
- 分析
- 首先我们考虑 如果 ar中没有k那么一定是 no
- 之后,我们发现 如果在序列ar中如果直接出现或间接通过变换出现相邻两个元素一个等于k,而另一个>= k,这种情况就一定是"yes",这样我们就可选这两个符合题意的相邻元素与旁边的一个元素元素进行边变换,这样得出了符合题意 更多的相邻的元素,这样一直循环下去就能得到 ar总所有的元素都变成 k 的情况
- 现在我们考虑什么样的情况可以通过 简介变换 的到 “符合题的相邻的两个元素(一个元素为k,另一个大于等于k)”,举例子说明:
- 我们设 ar序列为
1 0 2 0
, k = 1,这个时候明显 我们可以选中1 0 2
这三个元素变为1 1 1
, 这样更多“符合题的相邻元素”,就能把整个ar变得符合题意 - 我们在考
1 0 0 ...0 0 2 0 2
与1 0 0 ...0 0 2 2
,这两种情况 我们可通过分别选中子串202
、022
进行变换最终两种情况都会出现1 2 2 ... 2 2 2
,正好出现1 2
相邻,即出现“符合对相邻字符串” - 总结上面的直接 或 间接 变换情况 就可以得出:如果在ar中任意两个 >= k 的元素之间的距离小于等于2的话,就一定是yes
- 别忘了,讨论特殊情况1
- 我们设 ar序列为
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt", "w", stdout); }
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const int mxn = 2e5 + 10;
int ar[mxn];
int main()
{
/* fre(); */
int T;
scanf("%d", &T);
while(T --)
{
int n, k;
scanf("%d %d", &n, &k);
int fg = 0;
for(int i = 1; i <= n; i ++)
{
scanf("%d", &ar[i]);
if(ar[i] == k)
fg = 1;
}
if(! fg)
{
printf("no\n");
continue;
}
if(n == 1)
{
printf("yes\n");
continue;
}
fg = 0;
for(int i = 1; i < n; i ++)
{
if(ar[i] >= k)
{
if(ar[i + 1] >= k || (ar[i + 2] >= k && i + 2 <= n))
{
fg = 1;
break;
}
}
}
if(fg) printf("yes\n");
else printf("no\n");
}
return 0;
}