E:宝石组合
根据给的公式化简后变为gcd(a,b,c)根据算数基本定理,推一下就可以了
然后我们对1到mx的树求约数,并记录约数的次数,我们选择一个最大的且次数大于等3的就是gcd
int mx;
vector<int> g[N];
vector<int> cnt[N];
int n;
int a[N];
void solve()
{cin >> n;for (int i = 1; i <= n; i++){cin >> a[i];mx = max(mx, a[i]);}for (int i = 1; i <= mx; i++){for (int j = 1; j * i <= mx; j++){g[i * j].pb(i);}}vector<int> ans;sort(a + 1, a + 1 + n);for (int i = 1; i <= n; i++){int t = a[i];for (auto ed : g[t]){cnt[ed].pb(t);}}int res = 0;for (int i = 1; i <= mx; i++)if (cnt[i].size() >= 3)res = i;for (int i = 0; i < 3; i++)cout << cnt[res][i] << " ";
}
H:拔河
赛时写的记录全部区间和,然后sort判断区间是否相交,但是时间复杂度好像有点问题,应该不是很对。
这里看了别人写的复杂度的做法,具体说就是,我们首先枚举右端点再左端点,然后对于枚举的每一段区间我们只看右边即可了,因为左边的合理的方案在左边已经枚举过了,然后二分找大于等于当前的,这里我们也只需看右边就行,不需要在减一了原理和上面相同,然后我们枚举玩一个右端点后,把以下一个右段点为起点的区间全部删去,这样满足了下一个循环
注意1需要特殊处理一下,一开始就不存进set里面
const int N = 1003;
int a[N];
int n;
int s[N];
signed main()
{scanf("%d", &n);for (int i = 1; i <= n; i++)scanf("%d", &a[i]), s[i] = s[i - 1] + a[i];multiset<int> S;for (int l = 1; l <= n; l++) // 提前把1全删去了{for (int r = l + 1; r <= n; r++){S.insert(s[r] - s[l]);}}int res = 1e18;for (int r = 1; r < n; r++) // 但是l==1的时候并没有,所以一开始就调过1{for (int l = 1; l <= r; l++) // 本身也被r==l的时候删去了{int val = s[r] - s[l - 1];auto it = S.lower_bound(val);if (it != S.end()){int ans = abs(*it - val);res = min(res, ans);}if (it != S.begin()){it--;int ans = abs(*it - val);res = min(res, ans);}}for (int r2 = r + 1; r2 <= n; r2++) // 提前把下一个断点删去{S.erase(S.find(s[r2] - s[r]));}}printf("%lld\n", res);return 0;
}