Codeforces Round #641 (Div. 2)
Codeforces Round #641 (Div. 2)
A. Orac and Factors
题目大意
给定n和k,对n进行操作,给n加上n的所有因子中除去1的最小因子,然后进行k轮,问最终结果。
Solution
如果n是偶数,直接 + 2 * k .
如果n是奇数,它的因子必然是奇数,操作一次后就变成偶数,再按偶数操作即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//__builtin_popcount(n);
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define RI register int
const int MOD = 1e9 + 7;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int SZ = 2e6 + 10;
ll n,k;
int p[SZ],prime[SZ];
inline void s()
{
int tot = 0;
for(int i = 2;i <= 1000005;i ++ )
{
if(prime[i] == 0)
p[ ++ tot] = i;
for(int j = 1;j <= tot && i * p[j] <= n;j ++ )
{
prime[i * p[j]] = 1;
if(i % p[j] == 0) break;
}
}
}
int main()
{
int T;
s();
scanf("%d",&T);
while(T --)
{
scanf("%lld%lld",&n,&k);
if(n % 2 == 0) printf("%lld\n",2 * k + n);
else
{
for(int i = 2;p[i] <= n;i ++)
if(n % p[i] == 0)
{
n += p[i];
break;
}
printf("%lld\n",2 * (k - 1) + n);
}
}
return 0;
}
B. Orac and Models
题目大意
给定一个长度n的序列{s1,s2,…,sn},选择一个最长递增子序列,且满足每相邻的两个数,前者的下标可以整除后者,输出最长长度。
Solution
LIS的变式,对于当前数字,他只能选择连接在下标为它的因子的数后面,因子不容易列举,所以我们考虑刷表法。对于每一个位置,把它后面 能接的下标位置(即为该下标倍数的下标)都更新一次,最后找一个最大值作为答案。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//__builtin_popcount(n);
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define RI register int
const int MOD = 1e9 + 7;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int SZ = 2e5 + 10;
int n;
int dp[SZ],num[SZ],ans;
int main()
{
int T;
scanf("%d",&T);
while(T --)
{
ans = 0;
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
scanf("%d",&num[i]);
dp[i] = 1;
}
for(int i = 1;i <= n;i ++)
{
for(int j = i + i;j <= n;j += i)
if(num[j] > num[i]) dp[j] = max(dp[j],dp[i] + 1);
ans = max(ans,dp[i]);
}
printf("%d\n",ans);
}
return 0;
}
C. Orac and LCM
题目大意
给定一个长度为n的数组,求gcd { lcm (ai , aj) | i < j }
Solution
法一:
数组中只要有大于等于n - 1个数包含某个共同的因子,那么该因子就对答案有贡献。然后通过对每个数质因子分解记录一下每个质因子在每个数中的数量。如果这个质因子存在于大于等于n - 1个数中,那么考虑它对答案的贡献,应该乘上改质因子的几次方?据题意易得如果改质因子存在于 n个数中 ,则取第二个元素即次小幂次,如果存在n - 1个数中,则取第一个元素即最小幂次。
代码一
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//__builtin_popcount(n);
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define RI register int
const int MOD = 1e9 + 7;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int SZ = 2e5 + 10;
int n,maxn;
vector<int>divisor[SZ];
inline int pow_mod(int a,int b)
{
int res = 1;
while(b)
{
if(b & 1) res *= a;
a *= a;
b >>= 1;
}
return res;
}
inline void get_prime(int x)
{
for(int i = 2;i * i <= x;i ++)
{
if(x % i == 0)
{
int c = 0;
while(x % i == 0)
{
++ c;
x /= i;
}
divisor[i].push_back(c);
}
}
if(x != 1) divisor[x].push_back(1);
}
int main()
{
int x;
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
scanf("%d",&x);
maxn = max(maxn,x);
get_prime(x);
}
ll ans = 1;
for(int i = 2;i <= maxn;i ++)
{
if(divisor[i].size() == n)
{
sort(divisor[i].begin(),divisor[i].end());
ans *= pow_mod(i,divisor[i][1]);
}
if(divisor[i].size() == (n - 1))
{
sort(divisor[i].begin(),divisor[i].end());
ans *= pow_mod(i,divisor[i][0]);
}
}
printf("%lld\n",ans);
return 0;
}
法二
对于a1来说,产生了lcm(a1,a2) ,lcm(a1,a3) … lcm(a1,an)
a2: lcm(a2,a3),lcm(a2,a4) … lcm(a2,an) 依次类推
a1产生的lcm构成的gcd1为gcd(lcm(a1,a2) ,lcm(a1,a3) … lcm(a1,an))
a2产生的lcm构成的gcd2为gcd(lcm(a2,a3),lcm(a2,a4) … lcm(a2,an))
我们发现gcd1里取gcd的每一项都是a1的倍数,同理gcd2里gcd的每一项也是a2的倍数,依次类推。
所以 gcd( lcm(a1 , a2) , lcm(a1 , a3) … lcm(a1 , an) ) 可以转化为
lcm (a1 , gcd (a2 , a3 , … an) )
那么最后答案就为 ans = gcd( gcd1 , gcd2 , … gcdn ) 。
代码二
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//__builtin_popcount(n);
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define RI register int
const int MOD = 1e9 + 7;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int SZ = 1e5 + 10;
ll n,num[SZ],suff[SZ],gcd_i[SZ],ans = 0;
inline ll gcd(ll a,ll b)
{
if(b == 0) return a;
else return gcd(b,a % b);
}
inline ll lcm(ll a,ll b)
{
return (a * b) / gcd(a,b);
}
int main()
{
scanf("%lld",&n);
for(ll i = 1;i <= n;i ++) scanf("%lld",&num[i]);
for(ll i = n;i >= 1;i --) suff[i] = gcd(suff[i + 1],num[i]);
for(ll i = 1;i <= n;i ++) gcd_i[i] = lcm(num[i],suff[i + 1]);
for(ll i = 1;i <= n;i ++) ans = gcd(gcd_i[i],ans);
printf("%lld\n",ans);
return 0;
}
待更
D. Orac and Medians
题目大意
给定一个长度为n的序列,每次操作可以把任意区间的数全变成其中位数,当偶数个时,变成第(|s| + 1)/2小的元素,问能否经过多次操作,使得整个序列全部变成k。
Solution
首先,必须保证数列里面有k,否则非法。
我们令a b c分别为小于k的数,等于k的数,大于k的数。
很明显bc和cb均可转化为bb,故b附近的c全部可以变成b。
bac可以转化为bbb。
cac可以转化为ccc,通过两个连续的cc或者cac(可以转化为ccc),可以把整个序列一部分转化为c,当到k附近时,再把整个序列全部变成k。
得出结论:合法情况为当序列中存在k,且存在两个大于等于k的数字的距离小于等于2。
特例:n == 1 且存在k。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//__builtin_popcount(n);
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define RI register int
const int MOD = 1e9 + 7;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int SZ = 1e5 + 10;
int n,k;
int num[SZ];
inline bool check()
{
for(int i = 1;i < n;i ++)
{
if(num[i] >= k)
{
if(num[i + 1] >= k) return 1;
if(i + 2 <= n && num[i + 2] >= k) return 1;
}
}
return 0;
}
int main()
{
int T;
scanf("%d",&T);
while(T --)
{
bool flag = 0;
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;i ++)
{
scanf("%d",&num[i]);
if(num[i] == k) flag = 1;
}
if(flag == 0)
{
printf("no\n");
continue;
}
if(n == 1)
{
printf("yes\n");
continue;
}
if(check()) printf("yes\n");
else printf("no\n");
}
return 0;
}
E. Orac and Game of Life
题目大意
给定一个n * m的矩阵,0代表白色,1代表黑色。
矩阵进行迭代的规则:
1.如果一个方格周围没有一个与该格颜色相同的方格,那么这个方格的颜色不会改变。
(此处的相邻指有公共边)
2.否则黑变白,白变黑。
现在给定t次查询:每次查询给定i , j , q ,求整个矩阵经过 q 次迭代后,(i,j)的颜色(0/1)。
Solution
我们发现,当一个连通块开始变化后,接下来的每一轮都会进行相同的变化,但是对于其周围与它不同颜色的方格。在连通块颜色变化后,会变得与它颜色相同。即下一轮他们会一起变化。所以只要开始有连通块可以变化,那么就会在有限次次数内使得所有格子都开始变化。
所以我们开一个数组dep[i][j]表示(i,j)这个点在第几轮会开始变化,第一次就可以变换的联通块内的方格设置为1,其他的设置为INF,和dep == 1的点的最短距离就是(i,j)的dep,然后把dep为1的方格加入队列,开始BFS,直至更新完所有的点。
(注意开long long)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//__builtin_popcount(n);
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define RI register int
const int MOD = 1e9 + 7;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int SZ = 1000 + 10;
const int dx[4] = {0,0,1,-1};
const int dy[4] = {-1,1,0,0};
struct zt
{
int x,y;
};
int n,m,t;
int mp[SZ][SZ];
ll dep[SZ][SZ];
inline void bfs()
{
queue<zt> q;
for(int i = 1;i <= n;i ++) mp[i][0] = mp[i][m + 1] = 2;
for(int j = 1;j <= m;j ++) mp[0][j] = mp[n + 1][j] = 2;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++)
{
dep[i][j] = 2e18;
if(mp[i][j] == mp[i + 1][j] || mp[i][j] == mp[i - 1][j] || mp[i][j] == mp[i][j + 1] || mp[i][j] == mp[i][j - 1])
{
dep[i][j] = 1;
q.push((zt){i,j});
}
}
while(!q.empty())
{
zt now = q.front();
q.pop();
for(int i = 0;i < 4;i ++)
{
int xx = now.x + dx[i],yy = now.y + dy[i];
if(xx >= 1 && yy >= 1 && xx <= n && yy <= m)
{
if(dep[now.x][now.y] + 1 < dep[xx][yy])
{
q.push((zt){xx,yy});
dep[xx][yy] = dep[now.x][now.y] + 1;
}
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&t);
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++)
scanf("%1d",&mp[i][j]);
bfs();
ll a,b,c;
/*for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= m;j ++)
printf("%lld ",dep[i][j]);
printf("\n");
}
*/
for(int i = 1;i <= t;i ++)
{
scanf("%lld%lld%lld",&a,&b,&c);
ll opt = c - dep[a][b];
if(opt >= 0 && opt % 2 == 0) printf("%d\n",mp[a][b] ^ 1);
else printf("%d\n",mp[a][b]);
}
return 0;
}
2020.5.14