Codeforces Round #694 (Div. 2) D,F
- D.Strange Definition
- F.Strange Housing
D.Strange Definition
题意:
定义“相邻元素”:如果\(\frac{lcm(a,b)}{gcd(a,b)}\)是一个平方数,则称a,b相邻。
给一个长度为n序列a[i],d[i]为序列中与a[i]相邻元素的个数,这个序列每秒变化一次,每个元素变化方式为变成自己与自己所有相邻元素的乘积,现在有q次询问,求每次询问给一个x,输出第x秒序列中最大的d[i]。
题解:
1: \(\frac{lcm(a,b)}{gcd(a,b)} = \frac{a*b}{gcd(a,b)^2}\) 转化条件为: 将a,b质因数分解后,所有质因子在a,b中的奇偶性相同,则a,b相邻
2: 为了具体化这一条件,我们把一个数质因数分解,将所有出现次数为奇数次的因子相乘,比如 \(540=4^2*3^3*5^1\),出现奇数次的因子为3,5,得到\(3*5=15\),我们把它叫做“奇积”,可以发现不同的数得到的这个数如果相等,那他们一定相邻
3: 得到上面条件后,利用map将上面的数轻易分组,但要怎么处理变化后的数组呢,可以发现分组后大小为奇数的组别(除了“奇积”为1的)不管怎么变化,都不会和其他组相邻,大小为偶的和“奇积”为1的在一次变化后全部相邻,因此\(x==0\)时一个答案,\(x!=0\)时一个答案。
#include<bits/stdc++.h>
using namespace std;
#define f(i,a,b) for(register int i=a;i<=b;++i)
#define ll long long
#define N 300005
#define INF 999999999
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline ll read(){ll x;scanf("%lld",&x);return x;}
map<ll,ll>mmp;
ll ans1,ans2,Sum;
void Divide(ll x)
{
ll sum=1,y=x;
for(int i=2;i*i<=x;++i){
if(x%i==0){
int s=0;
while(x%i==0) s++,x/=i;
if(s&1) sum*=i;
}
}
if(x!=1) sum*=x;
mmp[sum]++;
}
void solve()
{
Sum=ans1=ans2=0;
mmp.clear();
ll n=read();
f(i,1,n) Divide(read());
for(auto mx:mmp)
{
ans1=max(ans1,mx.se);
if(((mx.se)%2==0)||mx.fi==1)
Sum+=mx.se;
}
ans2=max(ans1,Sum);
ll q=read();
f(i,1,q)
{
ll x=read();
if(!x) printf("%lld\n",ans1);
else printf("%lld\n",ans2);
}
}
int main()
{
ll T=read();
while(T--) solve();
return 0;
}
F.Strange Housing
PS:这题放F就离谱,明明很简单,无语
题意:给一个无向无权图,要求选取几个点染色使之满足以下条件:
1:每个点要么是染色点要么与被染色点相邻
2:图保持连通
3:染色点不能相邻
找出这个方案或输出“NO”
题解:从一个点出发dfs,vis标记是否遍历过,如果点的相邻点都没被染色,那这个点就染色,否则就不染色(染色vis[i]=2,不染色vis[i]=1),这条件1和条件3就肯定满足,至于条件2嘛,用十二指肠想想都知道肯定满足。
#include<bits/stdc++.h>
using namespace std;
#define f(i,a,b) for(register int i=a;i<=b;++i)
#define ll long long
#define N 300005
#define INF 999999999
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline ll read(){ll x;scanf("%lld",&x);return x;}
vector<ll>eg[N],Ans;
int vis[N];
void dfs(ll s)
{
vis[s]=2;
for(ll v:eg[s]){
if(vis[v]==2) vis[s]=1;
}
for(ll v:eg[s]) if(!vis[v]) dfs(v);
}
void solve()
{
ll n=read(),m=read();
Ans.clear();
f(i,1,n) vis[i]=0,eg[i].clear();
f(i,1,m){
ll x=read(),y=read();
eg[x].pb(y);
eg[y].pb(x);
}
dfs(1);
int sum=0;
f(i,1,n){
if(!vis[i]){
printf("NO\n");
return ;
}
if(vis[i]==2) Ans.pb(i);
}
printf("YES\n");
int Siz=Ans.size();
printf("%d\n",Siz);
for(ll x:Ans) printf("%lld ",x);printf("\n");
}
int main()
{
ll T=read();
while(T--) solve();
return 0;
}
本场比赛总结:
1:D题提到的“奇积”应该记住,在很多设计质因子奇偶性的问题时,“奇积”可以描述出这个性质
2:质因子的快速分解方法(适用于\(a_i<10^6\)时)
以前我采用的质因子分解都是一个一个\(n\sqrt{n}\)的爆炸性复杂度求解,当遇到\(n<10^6\)且\(a_i<10^6\)时轻易爆炸,如果预处理出1e6前每个数的最大因子,分解时只遍历因子,就可以\(nlogn\)求解,预处理复杂度为\(nlogn\),代码如下
int Div[N];
void Divide(int x)
{
while(Div[x])
{
int v=Div[x],sum=0;
while(x%v==0) sum++,x/=v;
ds.push_back(v);
zs.push_back(sum);
}
if(x!=1) ds.push_back(x),zs.push_back(1);
}
int main()
{
for(int i=2;i<=N;++i)
{
if(Div[i]) continue;
for(int j=i;j<=N;j+=i)
Div[j]=i;
}
}