NOI2010Day1

T1 能量采集

显然对于每个点采集时\(k = gcd(i, j) - 1\), 则

\[ans = \sum_{i = 1}^{n}\sum_{j = 1}^{m}(2 * gcd(i, j) - 1) \\ ans = 2 * (\sum_{i = 1}^{n}\sum_{j = 1}^{m}gcd(i, j)) - n * m \\ 欧拉反演得 \\ ans = 2 * (\sum_{d = 1}^{n}\phi(d)\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor) - n * m \]

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
#define N 100005

int n, m, prime[N], cnt = 0;

ll phi[N];

bool isprime[N];

void Init(int n)
{
	isprime[1] = 1;
	phi[1] = 1;
	for(int i = 2; i <= n; i++)
	{
		if(!isprime[i]) prime[++cnt] = i, phi[i] = i - 1;
		for(int j = 1; j <= cnt && i * prime[j] <= n; j++)
		{
			isprime[i * prime[j]] = 1;
			if(i % prime[j] == 0)
			{
				phi[i * prime[j]] = phi[i] * prime[j];
				break;
			}
			phi[i * prime[j]] = phi[i] * phi[prime[j]];
		}
	}
	for(int i = 2; i <= n; i++) phi[i] += phi[i - 1];
}

int main()
{
	cin >> n >> m;
	if(n > m) swap(n, m);
	Init(n);
	ll ans = 0;
	for(int l = 1, r; l <= n; l = r + 1)
	{
		r = min(n / (n / l), m / (m / l));
		ans += (phi[r] - phi[l - 1]) * (n / l) * (m / l);
	}
	printf("%lld\n", ans * 2 - 1ll * n * m);
	return 0;
}

T2 超级钢琴

对每个点找出范围里的最大值,然后每次取完之后把两边两段加入,在堆中按区间最大值排序取k个即可。

#include<bits/stdc++.h>

using namespace std;

#define N 500005
typedef long long ll;

int n, k, L, R;

ll a[N];

int st[20][N];

int Log2(int x) {return log(x) / log(2);}

int query(int l, int r)
{
	int len = log2(r - l + 1);
	return (a[st[len][l]] >= a[st[len][r - (1 << len) + 1]]) ? st[len][l] : st[len][r - (1 << len) + 1];
}

struct point
{
	int o, l, r, val;

	bool operator < (const point &s) const
	{
		return a[val] -  a[o - 1] < a[s.val] - a[s.o - 1];
	}
};

priority_queue <point> pq;

int main()
{
	scanf("%d%d%d%d", &n, &k, &L, &R);
	for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
	for(int i = 2; i <= n; i++) a[i] += a[i - 1];
	for(int i = 1; i <= n; i++) st[0][i] = i;
	for(int i = 1; i <= 19; i++)
		for(int j = 1; j <= n - (i << 1) + 1; j++)
		{
			if(a[st[i - 1][j]] >= a[st[i - 1][j + (1 << i - 1)]]) st[i][j] = st[i - 1][j];
			else st[i][j] = st[i - 1][j + (1 << i - 1)];
			// cout << i << ' ' << j << ' ' << a[st[i][j]] << endl;
		}
	for(int i = 1; i <= n; i++)
	{
		int l = i + L - 1, r = min(i + R - 1, n);
		if(l > n) break;
		pq.push((point){i, l, r, query(l, r)});		
	}	
	ll ans = 0;
	while(k--)
	{
		point u = pq.top();
		pq.pop();
		// cout << u.o << ' ' << u.val << endl;
		ans += a[u.val] - a[u.o - 1];
		int x = u.val;
		if(u.l <= x - 1) pq.push((point){u.o, u.l, x - 1, query(u.l, x - 1)});
		if(x + 1 <= u.r) pq.push((point){u.o, x + 1, u.r, query(x + 1, u.r)});
	}
	printf("%lld\n", ans);
	return 0;
}

T3 海拔

首先发现取值只会是0或1, 然后可知两者都会只有一个联通块,体力消耗就是两个联通块中间连着的那些边。所以起点和终点的最小割就是答案。平面图转对偶图求一求就好了

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
#define N 350005

int n, head[N], nxt[N << 2], to[N << 2], cnt = 0;

ll val[N << 2];

void add(int x, int y, ll z)
{
	cnt++;
	to[cnt] = y, val[cnt] = z, nxt[cnt] = head[x], head[x] = cnt;
}

ll dis[N];

bool vis[N];

struct point
{
	int x; ll dis;

	bool operator < (const point &o) const 
	{
		return dis > o.dis;
	}
};

priority_queue <point> q;

int s = 0, t;

ll ans = 0;

void dij()
{
	memset(dis, 0x3f, sizeof(dis));
	dis[s] = 0;
	q.push((point){s, 0});
	while(!q.empty())
	{
		point u = q.top(); q.pop();
		int x = u.x;
		if(vis[x]) continue;
		vis[x] = 1;
		for(int p = head[x]; p; p = nxt[p])
		{
			int v = to[p];
			// cout << ' ' << x << ' ' << v << endl;
			if(dis[v] > dis[x] + val[p])
			{
				dis[v] = dis[x] + val[p];
				q.push((point){v, dis[v]});
			}
		}
	}
	printf("%lld\n", dis[t]);
}

int main()
{
	scanf("%d", &n);
	t = n * n + 1;
	for(int i = 1; i <= n + 1; i++)	
		for(int j = 1; j <= n; j++)
		{
			int x;
			scanf("%d", &x);
			int k = (i - 1) * n + j;
			// cout << s << ' ' << k << ' ' << t << endl;
			if(i == 1) add(k, t, x);
			else if(i == n + 1) k -= n, add(s, k, x);
			else add(k, k - n, x);
		}
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n + 1; j++)
		{
			int x;
			scanf("%d", &x);
			int k = (i - 1) * n + j;
			if(j == 1) add(s, k, x);
			else if(j == n + 1) k--, add(k, t, x);
			else add(k - 1, k, x);
		}
	for(int i = 1; i <= n + 1; i++)	
		for(int j = 1; j <= n; j++)
		{
			int x;
			scanf("%d", &x);
			int k = (i - 1) * n + j;
			if(i == 1) add(t, k, x);
			else if(i == n + 1) k -= n, add(k, s, x);
			else add(k - n, k, x);
		}
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n + 1; j++)
		{
			int x;
			scanf("%d", &x);
			int k = (i - 1) * n + j;
			if(j == 1) add(k, s, x);
			else if(j == n + 1) k--, add(t, k, x);
			else add(k, k - 1, x);
		}
	// for(int i = s; i <= t; i++)
	// {
	// 	cout << i << ' ';
	// 	for(int p = head[i]; p; p = nxt[p])
	// 		cout << to[p] << ' ' << val[p] << ' ';
	// 	cout << endl;
	// }
	dij();
	return 0;
}
posted @ 2020-07-03 11:24  LJB00131  阅读(132)  评论(0编辑  收藏  举报