整体来讲挺水的。随便记录两道题吧。
C.Digital Graffiti
题目大意:
二维01矩阵,给你一个多边形。问你它有多少条边.
题目思路:
可以朴素的统计边。比较麻烦。这里给出一个比较巧妙的做法:
统计每四个相邻的格子中奇数个1的个数。
原理就是统计顶点个数(点个数=边个数).因为我们可以发现只有在 角的情况下,格子中才可能出现奇数个1.
10 00
11 01
F - GCD or MIN
题目大意:
给你一个数列。你每次可以拿两个数出来删掉,然后要么做 g c d gcd gcd要么做 m i n min min.得到结果再放入数列中。最后只会剩下一个数。问你这个数可能种数。
题目思路:
其实不难看出, m i n min min操作的结果就是删去一个数。且只有最小值不能被删去.且 g c d gcd gcd的操作也是单调递减的。所以最后这个数的大小一定小于等于 m i n { a i } min\{a_i\} min{ai}
所以问题可以转化为:让你从 n n n个数中选取出一个子集 g c d gcd gcd。问你有多少种结果 ≤ m i n { a i } \leq min\{a_i\} ≤min{ai}.
注:因为我们一旦从一个子集中gcd出一个 ≤ m i n { a i } \leq min\{a_i\} ≤min{ai}的数。接下来就是把他们全 m i n min min起来。这样就可以实现上述效果了。
如何求子集GCD的个数?
对于一个数 a i a_i ai,包含它的 g c d gcd gcd的最终结果一定是它的因子。
所以我们对每个数 a i a_i ai求解它的因数。对于每个因数 f i f_i fi,我们用map存下来有哪些 a i a_i ai中含有它(所以它们之间含有公共因子 f i f_i fi)。然后枚举因数,对对应的 a i a_i ai求一遍gcd,如果 = f i = f_i =fi,那么答案++.
时间复杂度:
O ( n m a x { a i } ) O(n\sqrt{max\{a_i\}}) O(nmax{ai})
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 2000 + 5;
const int mod = 1e9 + 7;
map<ll,vector<ll>> q;
int main()
{
ios::sync_with_stdio(false);
int n; cin >> n;
ll d = 1e9;
for (int i = 1 ; i <= n ; i++){
ll x ; cin >> x;
d = min(d , x);
for (ll j = 1 ; j * j <= x ; j++){
if (x % j == 0){
q[j].push_back(x);
if (j * j != x) q[x/j].push_back(x);
}
}
}
int ans = 0;
for (auto g : q){
if (g.first > d) break;
ll gcd = g.second[0];
for (auto k : g.second) gcd = __gcd(gcd , k);
if (gcd == g.first) ans++;
}
cout << ans << endl;
return 0;
}