打了网络赛,还可以,但是不知道哪里可以补题,就写写思路吧
这道题关键就是从特例推出普遍规律,先讨论简单的情况
稍微分析一下可以发现,只有2的次幂影响结果,所以设两数为pt 2pt
t为奇数,p为2的次幂,t不影响结果所以设t=1
如果是1 2 显然2的人赢
如果是4 2 2的人说自己i dont know那么到4的时候就知道自己不是1,4的人赢
如果是4 8 4的人说自己i dont know 8的人就知道自己不是2,8的人赢
总结一下,数字大的人赢,这里有点递推的感觉。
这题不难,不要有畏难情绪,自己给自己设限
稍微推一下,设l为l/x上取整,r为r/x上取整,那么K=2^(r-l-1)x(l+r)(r-l+1)
这个式子的变量是x,l和r是题目给的。可以发现x是K的因子,对于这个式子,只要确定了x,那么可以计算l和r,那么就可以验证是否等于K
因子需要求K的所有因子,K是10^14,直接根号K枚举的话加上多组数据会T。因此考虑对K进行质因数分解,通过枚举指数来枚举因子。
这里有两种方法,一种是预处理10^7的质数,用这些质数去质因数分解,一种是大数分解。
主要是熟悉板子
Miller_Rabin素数测试可以迅速测试一个大数(long long级别)是否为质数
pollard_rho算法可以对一个long long级别的数质因数分解。
注意板子中质因数分解的结果是无序重复的素数,如24就是2 3 2 2
- #include <cstdio>
- #include <ctime>
- #include <cstdlib>
- #include <algorithm>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
- const int S = 8;
-
- ll mult_mod(ll a, ll b, ll c)
- {
- a %= c;
- b %= c;
- ll ret = 0, tmp = a;
- while(b)
- {
- if(b & 1)
- {
- ret += tmp;
- if(ret > c) ret -= c;
- }
- tmp <<= 1;
- if(tmp > c) tmp -= c;
- b >>= 1;
- }
- return ret;
- }
-
- ll pow_mod(ll a, ll n, ll mod)
- {
- ll ret = 1;
- ll temp = a % mod;
- while(n)
- {
- if(n & 1) ret = mult_mod(ret, temp, mod);
- temp = mult_mod(temp, temp, mod);
- n >>= 1;
- }
- return ret;
- }
-
- bool check(ll a, ll n, ll x, ll t)
- {
- ll ret = pow_mod(a, x, n);
- ll last = ret;
- _for(i, 1, t)
- {
- ret = mult_mod(ret, ret, n);
- if(ret == 1 && last != 1 && last != n - 1) return true;
- last = ret;
- }
- if(ret != 1) return true;
- return false;
- }
-
- bool Miller_Rabin(ll n)
- {
- if(n < 2) return false;
- if(n == 2) return true;
- if((n & 1) == 0) return false;
- ll x = n - 1, t = 0;
- while((x & 1) == 0) x >>= 1, t++;
- srand(time(0));
-
- rep(i, 0, S)
- {
- ll a = rand() % (n - 1) + 1;
- if(check(a, n, x, t)) return false;
- }
- return true;
- }
-
- ll factor[100]; //储存质因数分解结果 有重复 无序
- int tol; //标号0~tol-1
-
- ll gcd(ll a, ll b)
- {
- ll t;
- while(b)
- {
- t = a;
- a = b;
- b = t % b;
- }
- if(a >= 0) return a;
- return -a;
- }
-
- ll pollard_rho(ll x, ll c)
- {
- ll i = 1, k = 2;
- srand(time(0));
- ll x0 = rand() % (x - 1) + 1;
- ll y = x0;
- while(1)
- {
- i++;
- x0 = (mult_mod(x0, x0, x) + c) % x;
- ll d = gcd(y - x0, x);
- if(d != 1 && d != x) return d;
- if(y == x0) return x;
- if(i == k) y = x0, k += k;
- }
- }
-
- void findfac(ll n, ll k)
- {
- if(n == 1) return;
- if(Miller_Rabin(n))
- {
- factor[tol++] = n;
- return;
- }
- ll p = n;
- int c = k;
- while(p >= n) p = pollard_rho(p, c--);
- findfac(p, k);
- findfac(n / p, k);
- }
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- ll n;
- scanf("%lld", &n);
- if(Miller_Rabin(n)) puts("Prime");
- else
- {
- tol = 0;
- findfac(n, 107);
- ll ans = factor[0];
- rep(i, 1, tol)
- ans = min(ans, factor[i]);
- printf("%lld\n", ans);
- }
- }
- return 0;
- }
二分答案读完题就知道了,关键是确定了最大值后,怎么操作使得删掉的边最少,这是这道题的关键。
这里要用到树形dp,dp[u]表示以u为根的子树符合条件的情况下,u所在的连通块的权值和是多少。
那么每次u自己和全部dp[v]算上,如果超过了最大值,那么就考虑删边。
为了使得删的边数最少,一次删边要去掉尽可能多的权值,所以删去最大的dp[v],还是超就删第二大的dp[v]以此类推。
这样子贪心下来就可以求出删了多少边了
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
- const int N = 2e5 + 10;
- int a[N], n, k, mx, cnt;
- vector<int> g[N];
- ll key, dp[N];
-
- void dfs(int u, int fa)
- {
- dp[u] = a[u];
- ll sum = a[u];
- vector<ll> ve;
- for(int v: g[u])
- {
- if(v == fa) continue;
- dfs(v, u);
- ve.push_back(dp[v]);
- sum += dp[v];
- }
-
- if(!ve.size()) return;
- sort(ve.begin(), ve.end(), greater<ll>());
- rep(i, 0, ve.size())
- {
- if(sum > key) sum -= ve[i], cnt++;
- else break;
- }
- dp[u] = sum;
- }
-
- bool check(ll x)
- {
- cnt = 0;
- key = x;
- dfs(1, 0);
- return cnt <= k;
- }
-
- int main()
- {
- int T, kase = 0;
- scanf("%d", &T);
- while(T--)
- {
- mx = 0;
- scanf("%d%d", &n, &k); k--;
- _for(i, 1, n) g[i].clear();
- _for(i, 1, n - 1)
- {
- int u, v;
- scanf("%d%d", &u, &v);
- g[u].push_back(v);
- g[v].push_back(u);
- }
- _for(i, 1, n) scanf("%d", &a[i]), mx = max(mx, a[i]);
-
- ll l = mx - 1, r = 1e15;
- while(l + 1 < r)
- {
- ll m = l + r >> 1;
- if(check(m)) r = m;
- else l = m;
- }
- printf("Case #%d: %lld\n", ++kase, r);
- }
-
- return 0;
- }
很容易发现操作几次就变1了,考虑怎么让它最小。
注意到每次操作都是变小的,所以如果一个数是最大的,大于另一个序列的最大值,那么它一定要变小,这个是必须要操作的。因此就根据这一点,比较两者的最大值,相等就不用操作,否则就变小,用优先队列实现。
cf题解的stl用的很秀啊,代码很短。
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- int n; scanf("%d", &n);
- vector<int> a(n), b(n);
- rep(i, 0, n) scanf("%d", &a[i]);
- rep(i, 0, n) scanf("%d", &b[i]);
-
- int ans = 0;
- priority_queue<int> qa(a.begin(), a.end());
- priority_queue<int> qb(b.begin(), b.end());
- while(!qa.empty())
- {
- if(qa.top() == qb.top())
- {
- qa.pop(); qb.pop();
- continue;
- }
- else if(qa.top() > qb.top())
- {
- qa.push(to_string(qa.top()).size());
- qa.pop(); ans++;
- }
- else
- {
- qb.push(to_string(qb.top()).size());
- qb.pop(); ans++;
- }
- }
- printf("%d\n", ans);
- }
-
- return 0;
- }
但我比赛时是另一个思路,大致是先去重,然后把二位数以上的操作,再去重,然后把非1的操作。但是我写去重的时候用了很多stl,常数很大,赛时AC,赛后T了……
其实就排个序然后用两个指针即可,不用弄那么复杂。改成这么写后AC了
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- const int N = 2e5 + 10;
- int a[N], b[N], visa[N], visb[N], n;
- vector<int> va, vb;
-
- int get(int x)
- {
- int res = 0;
- while(x)
- {
- x /= 10;
- res++;
- }
- return res;
- }
-
- void deal(int a[], int b[], int n)
- {
- _for(i, 1, n) visa[i] = visb[i] = 0;
- sort(a + 1, a + n + 1);
- sort(b + 1, b + n + 1);
-
- int pa = 1, pb = 1;
- while(pa <= n && pb <= n)
- {
- if(a[pa] == b[pb])
- {
- visa[pa] = visb[pb] = 1;
- pa++; pb++;
- }
- else if(a[pa] > b[pb]) pb++;
- else pa++;
- }
-
- va.clear();
- vb.clear();
- _for(i, 1, n)
- if(!visa[i])
- va.push_back(a[i]);
- _for(i, 1, n)
- if(!visb[i])
- vb.push_back(b[i]);
- }
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- scanf("%d", &n);
- _for(i, 1, n) scanf("%d", &a[i]);
- _for(i, 1, n) scanf("%d", &b[i]);
- deal(a, b, n);
-
- int ans = 0;
- rep(i, 0, va.size())
- if(va[i] >= 10)
- {
- va[i] = get(va[i]);
- ans++;
- }
- rep(i, 0, vb.size())
- if(vb[i] >= 10)
- {
- vb[i] = get(vb[i]);
- ans++;
- }
-
- int cnt = 0;
- for(int x: va) a[++cnt] = x;
- cnt = 0;
- for(int x: vb) b[++cnt] = x;
- deal(a, b, cnt);
-
- rep(i, 0, va.size())
- if(va[i] > 1)
- ans++;
- rep(i, 0, vb.size())
- if(vb[i] > 1)
- ans++;
- printf("%d\n", ans);
- }
-
- return 0;
- }
之前做过一道优点类似的,也是赢平局输,用dp做的,这题也类似。
考试时用dp过的,但是写的比较复杂,但大体思路和题解是一致的
我是在状态转移分类讨论的,而题解是直接取min max的,写起来很方便
令1为Alice,0为Draw,-1为Bob
那么Alice的目标是使值变大,Bob是使值变小,所以就转化成了博弈里面的min-max搜索。
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- const int N = 2000 + 10;
- int dp[N][N], n;
- char s[N];
-
- int check(char a, char b)
- {
- return a > b ? 1 : (a == b ? 0 : -1);
- }
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- scanf("%s", s + 1);
- n = strlen(s + 1);
-
- _for(i, 1, n - 1)
- {
- if(s[i] == s[i + 1]) dp[i][i + 1] = 0;
- else dp[i][i + 1] = 1;
- }
-
- for(int len = 4; len <= n; len += 2)
- _for(l, 1, n)
- {
- int r = l + len - 1;
- if(r > n) break;
-
- int cl = 1;
- if(dp[l + 1][r - 1] != 0) cl = min(cl, dp[l + 1][r - 1]);
- else cl = min(cl, check(s[l], s[r]));
- if(dp[l + 2][r] != 0) cl = min(cl, dp[l + 2][r]);
- else cl = min(cl, check(s[l], s[l + 1]));
-
- int cr = 1;
- if(dp[l + 1][r - 1] != 0) cl = min(cl, dp[l + 1][r - 1]);
- else cr = min(cr, check(s[r], s[l]));
- if(dp[l][r - 2] != 0) cl = min(cl, dp[l][r - 2]);
- else cr = min(cr, check(s[r], s[r - 1]));
-
- dp[l][r] = max(cl, cr);
- }
-
- if(dp[1][n] == 1) puts("Alice");
- else if(dp[1][n] == -1) puts("Bob");
- else puts("Draw");
- }
-
- return 0;
- }
还有一种O(n)的做法,比赛时我就猜测Bob不可能赢,我尝试构造了Bob赢得情况没构造出来。
如果有了这个结论,那么其实只要判断什么时候平局即可,不是平局就是Alice赢
如果是平局,那么就是两边拿到字符串是一样的,考虑什么时候会一样。
因为每次拿都是两侧的,或者一侧两个,那么需要它们是一样的。
两侧一样的就是对称的,一侧两个就是aabbcc这样
那么都存在呢,可以发现可以外面是对称的,里面是aabbcc的,那么判断是否是这样的串就可以了
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- const int N = 2000 + 10;
- char s[N];
- int n;
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- scanf("%s", s + 1);
- n = strlen(s + 1);
-
- int l = 1, r = n;
- while(l < r && s[l] == s[r]) l++, r--;
-
- int flag = 1;
- for(int i = l; i <= r; i += 2)
- if(s[i] != s[i + 1])
- {
- flag = 0;
- break;
- }
- puts(flag ? "Draw" : "Alice");
- }
-
- return 0;
- }
这种切割的,就设在以u为根的子树中,包含u的联通块即可
还要分当前联通块是否有一个黑色,然后分类讨论即可。
注意儿子所在联通块无黑色时不能切断
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
- const int N = 1e5 + 10;
- const int mod = 1e9 + 7;
- vector<int> g[N];
- ll dp[N][2];
- int c[N], n;
-
- ll binpow(ll a, ll b)
- {
- ll res = 1;
- for(; b; b >>= 1)
- {
- if(b & 1) res = res * a % mod;
- a = a * a % mod;
- }
- return res;
- }
-
- ll inv(ll x) { return binpow(x, mod - 2); }
-
- void dfs(int u, int fa)
- {
- ll res = 1;
- if(c[u] == 1)
- {
- for(int v: g[u])
- {
- if(v == fa) continue;
- dfs(v, u);
- if(c[v]) res = res * dp[v][1] % mod;
- else res = res * (dp[v][1] + dp[v][0]) % mod;
- }
- dp[u][1] = res;
- }
- else
- {
- for(int v: g[u])
- {
- if(v == fa) continue;
- dfs(v, u);
- if(c[v]) res = res * dp[v][1] % mod;
- else res = res * (dp[v][1] + dp[v][0]) % mod;
- }
- dp[u][0] = res;
-
- ll sum = 0;
- for(int v: g[u])
- {
- if(v == fa) continue;
- if(c[v]) sum = (sum + res) % mod;
- else sum = (sum + res * inv(dp[v][1] + dp[v][0]) % mod * dp[v][1] % mod) % mod;
- }
- dp[u][1] = sum;
- }
- }
-
- int main()
- {
- scanf("%d", &n);
- _for(i, 2, n)
- {
- int x; scanf("%d", &x); x++;
- g[x].push_back(i);
- g[i].push_back(x);
- }
- _for(i, 1, n) scanf("%d", &c[i]);
-
- dfs(1, 0);
- printf("%lld\n", dp[1][1]);
-
- return 0;
- }
从提问的方式可以看出是二分答案
二分一个答案,然后先删点。删点的实现可以用一个数组标记,后面遍历点的时候都要判断这是没有被删去的点。注意新图的点数要重新计算,不是n
得到一个新图后,这时候点权没有用了,只需要判断是否存在长度大于等于k的路径
如果有环则一定存在,否则用拓扑排序加dp求最长度。判环用拓扑排序。
注意有-1的情况,二分的边界要注意一下
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
- const int N = 2e5 + 10;
- vector<int> g[N];
- int a[N], d[N], dp[N], ban[N], n, m;
- ll k;
-
- bool check(int key)
- {
- int cnt = 0;
- _for(i, 1, n)
- {
- if(a[i] <= key) ban[i] = 0, cnt++, dp[i] = 1;
- else ban[i] = 1;
- d[i] = 0;
- }
-
- _for(u, 1, n)
- for(int v: g[u])
- if(!ban[u] && !ban[v])
- d[v]++;
-
- queue<int> q;
- vector<int> topo;
- _for(i, 1, n)
- if(!ban[i] && !d[i])
- q.push(i);
- while(!q.empty())
- {
- int u = q.front(); q.pop();
- topo.push_back(u);
- for(int v: g[u])
- if(!ban[v] && --d[v] == 0)
- q.push(v);
- }
- if(topo.size() < cnt) return true;
-
- int res = 0;
- for(int u: topo)
- for(int v: g[u])
- if(!ban[v])
- dp[v] = max(dp[v], dp[u] + 1);
- for(int u: topo) res = max(res, dp[u]);
- return res >= k;
- }
-
- int main()
- {
- scanf("%d%d%lld", &n, &m, &k);
- _for(i, 1, n) scanf("%d", &a[i]);
- while(m--)
- {
- int u, v;
- scanf("%d%d", &u, &v);
- g[u].push_back(v);
- }
-
- int l = 0, r = 1e9 + 1;
- while(l + 1 < r)
- {
- int m = l + r >> 1;
- if(check(m)) r = m;
- else l = m;
- }
- printf("%d\n", r == 1e9 + 1 ? -1 : r);
-
- return 0;
- }
先考虑简单的情况,c全是0
可以发现确定了一个数,就有一堆数连着被确定,形成一个环。
于是ai和bi连边,就会形成很多个环。
环中的一个点确定了就都确定了,可以确定使用a或b
因此每一个大于1的环就有两种选择
对于c已经有数,就是已经确定了,不对答案有贡献。
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
- const int mod = 1e9 + 7;
- const int N = 1e5 + 10;
- int f[N], a[N], b[N], ban[N], siz[N], n;
-
- ll binpow(ll a, ll b)
- {
- ll res = 1;
- for(; b; b >>= 1)
- {
- if(b & 1) res = res * a % mod;
- a = a * a % mod;
- }
- return res;
- }
-
- int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
-
- void Union(int x, int y)
- {
- int fx = find(x), fy = find(y);
- if(fx == fy) return;
- siz[fx] += siz[fy];
- f[fy] = fx;
- }
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- scanf("%d", &n);
- _for(i, 1, n) f[i] = i, ban[i] = 0, siz[i] = 1;
- _for(i, 1, n) scanf("%d", &a[i]);
- _for(i, 1, n) scanf("%d", &b[i]);
-
- _for(i, 1, n) Union(a[i], b[i]);
- _for(i, 1, n)
- {
- int x; scanf("%d", &x);
- ban[find(x)] = 1;
- }
-
- int cnt = 0;
- _for(i, 1, n)
- if(f[i] == i && !ban[i] && siz[i] > 1)
- cnt++;
- printf("%lld\n", binpow(2, cnt));
- }
-
- return 0;
- }
这题的关键就是像上一题一样,ai向bi连边可以形成环。如果没有做过上一题,很难想到
有环之后考虑怎么分配数字。一个直觉的想法是一大一小分配,这样让差最大,如何证明呢?
当存在a1 > a2 > a3时,对答案的贡献是等价于a1 > a3的,也就是中间的a2并没有用。那么对于任意一种分配,删去这种没有用的点后,一定是一大一小交替。而且可以发现,一大一小交替的话,个数一定是偶数。
这时大的绝对值取正,小的绝对值取负,所以我们让大的尽可能大,小的尽可能小。
因此,如果环是偶数,那么就大的位置放尽可能大的,小的放尽可能小的。
对于环是奇数,有一个位置是没有用的,所以要填一个最差的,也就是中间的值,把极值留给其他地方。
最后注意开long long
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
- const int N = 1e5 + 10;
- int f[N], a[N], b[N], v[N], n, l, r, pos;
- vector<int> k[N], num;
-
- int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
-
- void Union(int x, int y) { f[find(x)] = find(y); }
-
- ll cal(int x)
- {
- if(x <= 1) return 0;
- if(x % 2 == 1) x--;
- rep(i, 0, x) v[i] = num[pos++];
-
- ll res = 0;
- rep(i, 0, x)
- {
- int j = (i + 1) % x;
- res += abs(v[j] - v[i]);
- }
- return res;
- }
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- scanf("%d", &n);
- _for(i, 1, n) f[i] = i, k[i].clear();
- _for(i, 1, n) scanf("%d", &a[i]);
- _for(i, 1, n) scanf("%d", &b[i]);
-
- _for(i, 1, n) Union(a[i], b[i]);
- _for(i, 1, n) k[find(i)].push_back(i);
-
- ll ans = 0;
- l = 1, r = n;
- num.clear(); pos = 0;
- while(l <= r)
- {
- num.push_back(l); l++;
- num.push_back(r); r--;
- }
-
- _for(i, 1, n) ans += cal(k[i].size());
- printf("%lld\n", ans);
- }
-
- return 0;
- }
首先注意一波推导,注意余数取k而不是取0,因为数字要是正数,同时两边乘以2去掉分数
可以得到2n = k(k + 1 + 2t)
我自己做的时候推到这里就不知道怎么分析了
首先注意k和k+1+2t,一个是偶数一个奇数
也就是说2n可以分解为一个偶数和一个奇数相乘。
因为奇数部分没有2的因子
所以就直接把2n一直除以2来分解
然后就成了一个奇数和偶数。但是注意奇数部分不能为1,因为k和k+1+2t不可能为1
分解完之后,哪一个是k呢,注意到k+1+2t更大,所以k就取奇数和偶数更小的那个即可
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- typedef long long ll;
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- ll n;
- scanf("%lld", &n);
- n *= 2;
-
- ll t = 1;
- while(n % 2 == 0)
- {
- n /= 2;
- t *= 2;
- }
-
- if(n == 1) puts("-1");
- else printf("%lld\n", min(t, n));
- }
-
- return 0;
- }
首先可以发现,新出现的数肯定是当前数子集的gcd
然后每个数都是不同的,所以当前序列没有的数很少,那么就去枚举这些输数,然后枚举它的倍数,然后将它的倍数取gcd,如果是它那么就答案加一。
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- unordered_map<int, bool> vis;
- int n, mx;
-
- int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
-
- int main()
- {
- scanf("%d", &n);
- _for(i, 1, n)
- {
- int x; scanf("%d", &x);
- vis[x] = 1;
- mx = max(mx, x);
- }
-
- int ans = 0;
- _for(i, 1, mx)
- if(!vis[i])
- {
- int cur = 0;
- for(int j = i; j <= mx; j += i)
- if(vis[j])
- cur = gcd(cur, j);
- if(cur == i) ans++;
- }
- printf("%d\n", ans);
-
- return 0;
- }
一开始的想法是枚举当前数的因数,然后用这个因数看是多少个数的公因数
一次的复杂度是max(根号ai,因数个数 * n) 我一开始算成根号ai*n了,然后觉得会T
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- const int N = 50;
- int a[N], n;
- unordered_map<int, int> mp;
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- mp.clear();
- scanf("%d", &n);
-
- int flag = 0;
- _for(i, 1, n)
- {
- scanf("%d", &a[i]);
- if(++mp[a[i]] >= n / 2) flag = 1;
- }
- if(flag)
- {
- puts("-1");
- continue;
- }
-
- int ans = 1;
- _for(id, 1, n)
- {
- vector<int> ve;
- _for(j, 1, n)
- if(a[j] - a[id] >= 0)
- ve.push_back(a[j] - a[id]);
-
- if(ve.size() < n / 2) continue;
-
- rep(i, 0, ve.size())
- {
- for(int j = 1; j * j <= ve[i]; j++)
- if(ve[i] % j == 0)
- {
- int cnt = 0;
- rep(k, 0, ve.size())
- if(ve[k] % j == 0)
- cnt++;
- if(cnt >= n / 2) ans = max(ans, j);
-
- cnt = 0;
- rep(k, 0, ve.size())
- if(ve[k] % (ve[i] / j) == 0)
- cnt++;
- if(cnt >= n / 2) ans = max(ans, ve[i] / j);
- }
- }
- }
- printf("%d\n", ans);
- }
-
- return 0;
- }
题解的方法优美一些,把所有因子扔到map里面统计一下有无超过一半,注意相同的情况。
- #include <bits/stdc++.h>
- #define rep(i, a, b) for(int i = (a); i < (b); i++)
- #define _for(i, a, b) for(int i = (a); i <= (b); i++)
- using namespace std;
-
- const int N = 50;
- int a[N], n;
- unordered_map<int, int> mp, vis;
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- mp.clear();
- scanf("%d", &n);
-
- int flag = 0;
- _for(i, 1, n)
- {
- scanf("%d", &a[i]);
- if(++mp[a[i]] >= n / 2) flag = 1;
- }
- if(flag)
- {
- puts("-1");
- continue;
- }
-
- int ans = 1;
- _for(id, 1, n)
- {
- int same = 0;
- vector<int> ve;
- _for(j, 1, n)
- {
- if(a[j] - a[id] == 0) same++;
- else if(a[j] - a[id] > 0) ve.push_back(a[j] - a[id]);
- }
-
- vis.clear();
- rep(i, 0, ve.size())
- {
- for(int j = 1; j * j <= ve[i]; j++)
- if(ve[i] % j == 0)
- {
- vis[j]++;
- if(j * j != ve[i]) vis[ve[i] / j]++;
- }
- }
- for(auto x : vis)
- if(x.second + same >= n / 2)
- ans = max(ans, x.first);
- }
- printf("%d\n", ans);
- }
-
- return 0;
- }