Task 1:锦标赛
题目描述:
给出2^n个数,进行n此操作。每次操作对当前剩下的数两两比较(任意组合都可以),留下较大的那个。现在询问对于每个数,做多可以进行多少操作。
题目分析:
我们可以很显然发现,按照题意进行操作我们可以得到一颗完全二叉树。所以对于一个数a,有b个数小于等于a,那么ans=log2(b)。
Code:
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:
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;
}