2017-8-30 NOIP冲刺

xiaoxiao2021-02-28  116

Task 1:锦标赛

题目描述:

给出2^n个数,进行n此操作。每次操作对当前剩下的数两两比较(任意组合都可以),留下较大的那个。现在询问对于每个数,做多可以进行多少操作。

题目分析:

我们可以很显然发现,按照题意进行操作我们可以得到一颗完全二叉树。所以对于一个数a,有b个数小于等于a,那么ans=log2(b)。

Code:

#include <cmath> #include <cstdio> #include <algorithm> using namespace std; const int MAXN = 1<<20; int b, n; int a[MAXN+5], q[MAXN+5]; int main(){ scanf("%d",&b), n = 1<<b; for(int i = 1; i <= n; ++ i) scanf("%d",&a[i]), q[i] = a[i]; sort(q+1, q+n+1); for(int i = 1; i <= n; ++ i){ int p = upper_bound(q+1, q+n+1, a[i])-q-1; printf("%d",log2(p)); if(i != n) putchar(32); else putchar(10); } return 0; }

Task 2:不完美值

题目描述:

给出数组a[],区间[l,r]。令xi表示sigma(b) (b|a[i] && b!=a[i])。求sigma(abs(ai-xi)) (l<=i<=r)。

题目分析:

这道题就是裸的求一个数的真因子和。但是考虑到做多有10^7个数,所以不能暴力的对每个数用sqrt(n)的时间来求,这样肯定会T。 所以我们就需要一个线性的算法。 我们定义f[i]表示i的因子和,p是素数且p|i。 所以f[i]=(1+p1+p1^2+……p1^k1)*(1+p2+p2^2+……p2^k2)*……*(1+pn+pn^2+……pn^kn)。 这样我们就可以用类似筛法来求了。

Code:

#include <cstdio> #define abs(v) ((v)<0 ? -(v):(v)) int l, r; long long f[10000005], ans; inline void getans(int n){ f[1] = 1; int tmp, sum, d; for(int i = 2; i <= n; ++ i){ if(!f[i]){ for(int j = 2; j*i <= n; ++ j){ tmp = j*i, sum = 0, d = i; while(tmp%i == 0) tmp /= i, sum += d, d *= i; if(!f[j*i]) f[j*i] = 1; f[j*i] *= sum; } f[i] = i+1; } if(i >= l) ans += abs(i+i-f[i]); } } int main(){ scanf("%d%d",&l,&r); getans(r); printf("%I64d\n",ans); return 0; }

Task 3:糖果

题目描述:

给出n(n<10^5)个节点,每个节点有一个权值p(p<10^7)。连接两个节点i,j的代价为min(pi%pj,pj%pi)。 求将这些节点连成一棵树的最小代价。

题目分析:

首先我们可以很显然的看出这道题就是一道最小生成树,但如果暴力建树的话,最多就会有n^2条边。 就算不考虑时间,但是空间上也过不了。 所以解题的关键就在于怎样把一些一定不会被用到的边删去,在连边的时候只连那些有可能被选中的边。 按照这样想下去。 如果我们先把点按从小到大排序。那么在i,j(i<j)之间连边的代价就一定是pj%pi。 那么我们可以这样考虑:将数轴pi等分。 那么在每个区间内对于i来说,如果连的边出现在最小生成树中的话,那么i只可能和这个区间内%pi最小的那个。 这样边就被优化成了n*log2(10^7)条,这样就可以过了。

Code:

#include <cstdio> #include <cstring> #include <cassert> #include <algorithm> #include <iostream> #include <vector> #include<fstream> #include<cstdlib> using namespace std; #define TRACE(x) cerr << #x << " = " << x << endl #define REP(i, n) for (int i=0; i<n; i++) #define FOR(i, a, b) for (int i=(a); i<(b); i++) typedef long long ll; typedef pair<int, int> P; #define X first #define Y second const int MAXN = 1<<19, MAXM = 1e7 + 5; vector <P> V[MAXM]; int najbliz[MAXM]; int p[MAXN]; int n; int root[MAXN], rnk[MAXN]; int bio[MAXM], ozn[MAXM]; int find(int x) { if (root[x] == -1) return x; return root[x] = find(root[x]); } void merge(int a, int b) { a = find(a); b = find(b); assert(a != b); if (rnk[a] > rnk[b]) root[b] = a; else if (rnk[b] > rnk[a]) root[a] = b; else { rnk[a]++; root[b] = a; } } int main() { freopen("sirni.in","r",stdin); freopen("sirni.out","w",stdout); scanf("%d", &n); memset(ozn, -1, sizeof ozn); REP(i, n) { scanf("%d", &p[i]); najbliz[p[i]] = p[i]; if (ozn[p[i]] == -1) ozn[p[i]] = i; } for (int i=MAXM-2; i>=0; i--) if (!najbliz[i]) najbliz[i] = najbliz[i+1]; REP(i, n) { if (bio[p[i]]++) continue; if (najbliz[p[i]+1]) { if (2*p[i] >= MAXM || najbliz[2*p[i]] != najbliz[p[i]+1]) V[najbliz[p[i]+1] - p[i]].push_back(P(i, ozn[najbliz[p[i]+1]])); } for (int j=2*p[i]; j<MAXM && najbliz[j]; j+=p[i]) if (j + p[i] >= MAXM || najbliz[j+p[i]] != najbliz[j]) V[najbliz[j]-j].push_back(P(i, ozn[najbliz[j]])); } memset(root, -1, sizeof root); ll rje = 0; REP(i, MAXM) REP(j, (int) V[i].size()) if (find(V[i][j].X) != find(V[i][j].Y)) { merge(V[i][j].X, V[i][j].Y); rje += i; } REP(i, n) if (ozn[p[i]] == i) { assert(find(i) == find(0)); } printf("%lld\n", rje); return 0; }
转载请注明原文地址: https://www.6miu.com/read-58282.html

最新回复(0)