2017-8-30 NOIP冲刺

Task 1:锦标赛

题目描述:

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

题目分析:

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

Code:

#include 
#include 
#include 
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<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 
#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

Code:

#include 
#include 
#include 
#include 
#include 
#include 
#include
#include


using namespace std;

#define TRACE(x) cerr << #x << " = " << x << endl
#define REP(i, n) for (int i=0; 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 

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]; jif (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; }

你可能感兴趣的:(NOIP,C++,NOIP模拟赛,NOIP冲刺)