目录
A. Hossam and Combinatorics
题目大意:
给定一个长度为n的数组,求数组中两个元素相减绝对值最大的个数。一对下标pair(i, j) 和pair(j, i)都是有贡献的。
思路:
统计最大值和最小值的个数,贡献值就是最大值的数量 * 最小值的数量 * 2, 但是要注意最大值和最小值可能是同一个值,所以我们需要特判一下,具体细节看代码。
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define all(x) (x).begin(),(x).end()
void solve()
{
int n;
cin >> n;
vector<int> a(n), cnt(100010);
rep(i, 0, n - 1) cin >> a[i], cnt[a[i]]++;
sort(all(a));
if (a[0] == a[n - 1]) {
cout << 1ll * n * (n - 1) << '\n';
return;
}
cout << 1ll * 2 * cnt[a[n - 1]] * cnt[a[0]] << '\n';
}
B. Hossam and Friends
题目大意:
有n个小朋友,编号1~n,给m组关系,每组关系给定两个小朋友的编号x, y, 表示x与y不认识。定义一个子段是好的:在区间[a, b], a,a+1,a+2,...b都互相认识。求有多少个好的子段。
思路:
发现前面的小朋友想跟后面的小朋友构成好的子段,必须后面的小朋友都要相互认识,所以我们考虑从大到小枚举小朋友的编号,动态维护每个小朋友能够向右扩展子段长度的最大编号。但是比赛的时候我是把所有的关系全部用vector存起来了,其实只要维护每个小朋友不认识的朋友的编号最小值。
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
void solve()
{
int n, m;
cin >> n >> m;
vector<int> a[n + 1];
rep(i, 1, m) {
int l, r;
cin >> l >> r;
if (l > r) swap(l, r);
a[l].pb(r);
}
ll ans = 0;
int last = n + 1;//表示初始都可以构成好的子段
for (int i = n; i >= 1; i--) if (SZ(a[i])) {
sort(all(a[i]));//这里其实可以只维护一个最小值
last = min(last, a[i][0] - 1);
ans += last - i + 1;
}
else {
if (last == n + 1) ans += (n - i + 1);
else ans += (last - i + 1);
}
cout << ans << '\n';
}
C. Hossam and Trainees
题目大意:给定一个长度为n的a数组,询问是否存在两个数的最大公约数不为1.如果存在输出YES,否则输出NO。1 <= ai <= 1e9
思路:赛时忘记写一个预判,一直wa... 可以考虑筛出40000内的所有质数,然后枚举这些质数x,看是否存在有两个数都可以整除x,并且用一个vector存储x,表示该下标的值有x的质因子,如果存在的话就可以输出YES,否则如果还存在YES的情况,那就是存在两个数有大于40000的质因子。先把所有的a[i]值用map容器标记,我们枚举每个数,然后整除用之前vector容器存储的质因子,看最后被整除的数是否出现过,如果出现过,我们就输出YES,否则输出NO。
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define x first
#define y second
#define SZ(x) ((int)(x).size())
using namespace std;
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
const ll mod=1000000007;
const int N = 100010;
int primes[N];
int cnt;
bool st[N];
//筛质数
void is_prime(int n)
{
for(int i = 2;i <= n; i++)
{
if(!st[i])
{
primes[cnt++] = i;
}
for(int j = 0; j < cnt && primes[j] <= n / i; j++)
{
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
void solve()
{
int n;
cin >> n;
vector<int> a(n);
rep(i, 0, n - 1) cin >> a[i];
sort(all(a));
vector<int> c[n + 1];
//存储每个a[i]有的质因子(<=10000)。
for (int i = 0; i < cnt; i++) {
int num = 0;
for (int j = n - 1; j >= 0; j--)
if ((a[j] % primes[i]) == 0) {
c[j].push_back(primes[i]);
num ++;
if (num >= 2) {
cout << "YES" << '\n';
return;
}
}
}
map<int, int> mp;
for (int i = 0; i < n; i++) mp[a[i]] = 1;
for (int i = n - 1; i >= 0; i--) {
bool ok = false;
int len = (int)c[i].size();
for (int j = 0; j < len; j++) {
while (a[i] % c[i][j] == 0) {
a[i] /= c[i][j];
ok = true;
}
}
//注意我们这里需要用一个ok标记一下是因为a[i]之前已经被标记过了
//如果我们不存在小于40000的质因子,那么a[i]这个值就是本身 如果不加
//ok那么就一个数算了两次
if (ok && a[i] > 1 && mp[a[i]]) {
cout << "YES" << '\n';
return;
}
else mp[a[i]] = 1;
}
cout << "NO" << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
is_prime(40000);
int t;
cin >> t;
while(t--)
solve();
return 0;
}