CF837A-D
A. Hossam and Combinatorics
题解:记录序列里面不同值出现的次数,如果序列里面只有一个值出现n次,则答案为n*(n-1),否则,答案为Count(max)*Count(min)*2,记得开long long
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6;
int n,a[N];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T=1;
cin>>T;
while(T--)
{
cin>>n;
int maxx=0,minn=1e9;
map<int,int>mp;
for(int i=1;i<=n;i++)
{
cin>>a[i];
maxx=max(a[i],maxx);
minn=min(minn,a[i]);
mp[a[i]]++;
}
if(maxx!=minn)
cout<<mp[maxx]*mp[minn]*2<<"\n";
else
cout<<mp[maxx]*(mp[maxx]-1)<<"\n";
}
return 0;
}
B. Hossam and Friends
题解:预处理a[i]表示比i大的且跟i不是朋友关系的编号最小的人,单纯然后对每个i去求的话,可能会有不可增加的段数被增加了,例如若a[i]=j,而在i和j之间可能还有x和y互相不是朋友(a[x]=y,且i<x<y<j),这样a[i]的贡献就不是j-i了,就应该是y-i,这样的话不妨让a[i]=y
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6;
int n,a[N],m;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T=1;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
a[i]=n+1;
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
if(x<y)
{
a[x]=min(a[x],y);
}
else
{
a[y]=min(a[y],x);
}
}
int ans=0;
for(int i=n-1;i>=1;i--)
a[i]=min(a[i],a[i+1]);//这一步减少无效段数很重要
for(int i=1;i<=n;i++)
ans+=a[i]-i;
cout<<ans<<"\n";
}
return 0;
}
C. Hossam and Trainees
题解:题意意思是,给你n个数,如果存在有两个数不互质,那么输出YES,否则输出NO,观察到这n个数最大为1e9,有一个数论小知识(已纠正):任何合数X的所有质因数都不会超过他的。那么我们可以预处理之内的所有质数,然后用一个map来储存当前拥有质数的个数。每次输进来一个数x,对他进行质因数分解,并且记录质数。当有记录的质数的个数等于两个时,则存在有两个数他们分解的质因数有相同的值,这时他们一定不互质,则输出YES。
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e7;
int a[N],b[N],n,m,x,y,z,t,k,q;
int ans,res;
int primes[N], cnt; // primes[]存储所有素数
bool st[N]; // st[x]存储x是否被筛掉
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T=1;
get_primes(int(sqrt(1e9)));//预处理出质数进行质因数分解
cin>>T;
while(T--)
{
map<int,int>mp;
cin>>n;
bool ok=true;
for(int i=1; i<=n; i++)
{
cin>>x;
if(ok)
{
//从0开始,到第cnt-1个结束,避免RE
for (int j = 0; primes[j]*primes[j]<=x&&j<cnt; j ++ )
{
int k=primes[j];
if (x % k == 0)
{
while (x % k == 0) x /= k;
mp[k]++;
if(mp[k]==2)
{
ok=false;
break;
}
}
}
}
if(x>1)mp[x]++;
if(mp[x]==2)ok=false;
}
if(ok)cout<<"NO"<<"\n";
else cout<<"YES"<<"\n";
}
return 0;
}
D. Hossam and (sub-)palindromic tree
题解:
题意是给你一棵树,每个结点都有一个字符,任意两个结点之间的距离,距离经过的点和这两个点构成一个字符序列,求这个树里面,所有字符序列中回文序列个数最多的,求该最多个数数量。注意是字符序列,序列里面的回文序列,不是回文串,也就是说一段序列abxyab中,他是存在回文序列的,为abab,长度为4,先求所有序列中长度最长的。
记录一下做法,先n次dfs预处理出,以点i为根,所有节点的父亲节点为多少,即fa[i][j] :表示以j为根节点,i的父亲节点为fa[i][j]。然后为了求最长的回文串,对任意两对节点,都需要求一遍,他们路径上的最长回文串。
用递归的做法,再加上记忆化,详细见代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
int n;
string s;
vector<int>v[N];
int dp[2010][2010];
int fa[2010][2010];
void dfs(int x,int y,int z)
{
fa[x][z]=y;
for(auto w:v[x])
{
if(w==y)continue;
dfs(w,x,z);
}
}
int ddfs(int x,int y)//求x到y的路径上最长的回文序列
{
if(x==y)//若x等于y,则只有一个字符,最长回文序列长度为1
return 1;
if(fa[x][y]==y)//若x的父亲就是y,直接判断即可
return ((s[x]==s[y])? 2 : 1);
if(dp[x][y]!=-1)//记忆化
return dp[x][y];
int anss=0;
//x与y之间的路径,可以分为以x为树根或者以y为树根两种情况
anss=max(ddfs(fa[x][y],y),ddfs(x,fa[y][x]));
//若x和y的字符相同,则只需考虑彼此父亲的情况
if(s[x]==s[y])
anss=max(anss,ddfs(fa[x][y],fa[y][x])+2);
return dp[x][y]=anss;//记忆化
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while(T--)
{
cin>>n;
cin>>s;
for(int i=1;i<=n;i++)
{
v[i].clear();
for(int j=1;j<=n;j++)
{
dp[i][j]=-1;
fa[i][j]=0;
}
}
s=" "+s;
for(int i=1;i<=n-1;i++)
{
int x,y;
cin>>x>>y;
v[x].push_back(y);
v[y].push_back(x);
}
int ans=1;
for(int i=1;i<=n;i++)//预处理
dfs(i,0,i);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
ans=max(ans,ddfs(i,j));
}
}
cout<<ans<<"\n";
}
return 0;
}