国庆七天乐Day1 - 2015ICPC长春站 【8/13】

题目链接

A - Too Rich

有面值为1.5.10.20.50.100.200.500.1000.2000的钞票若干张,要求用尽可能多数目的钞票凑出p元。

考虑先求出给出的所有金额之和sum,那么问题就等价于用尽可能少的钞票凑出sum-p元。如果每一种面值都能整除较小的那个面值,那么只要直接贪心即可。但此题由于50和500的存在不能直接贪心,例如:p = 600,手中有3张200和1张500。

此时如果贪心的话是凑不出600的,但实际上可以。考虑到这种情况的存在,在从后往前遍历每种面值的钞票的时候,都分为全部使用与留一张这两种情况,可以用dfs实现。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;

const int maxn = 2505;
const ll INF = (1LL << 62) - 1;

int t;
ll a[15], p, ans;
ll w[15] = {1, 5, 10, 20, 50, 100, 200, 500, 1000, 2000};

void dfs(int u, ll p, ll res)
{
    if(u < 0)
    {
        if(p == 0) ans = min(ans, res);
        return;
    }
    ll tmp = p/w[u];
    tmp = min(tmp, a[u]);
    dfs(u-1, p - tmp*w[u], res + tmp);
    if(tmp > 0)
        dfs(u-1, p - (tmp-1)*w[u], res + tmp - 1);
}

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%lld", &p);
        ll sum = 0,cnt = 0;
        for(int i = 0;i < 10;i++)
        {
            scanf("%lld", &a[i]);
            sum += a[i]*w[i];
            cnt += a[i];
        }
        if(sum < p) {puts("-1"); continue;}
        sum -= p;
        ans = INF;
        dfs(9, sum, 0);
        if(ans == INF) puts("-1");
        else printf("%lld\n", cnt - ans);
    }
    return 0;
}

B - Count a*b

定义:f(n)=\sum _{i=0}^{n-1}\sum _{j=0}^{n-1}[(i*j) modn != 0],g(n)=\sum _{m|n}^{ }f(m),求g(n)。

设:h(n)=\sum _{i=0}^{n-1}\sum _{j=0}^{n-1}[(i*j) modn == 0],那么f(n)=n^{2}-h(n)。考虑化简h(n):

h(n)=\sum _{i=1}^{n}\sum _{j=1}^{n}[gcd(i*j, n)==n]=\sum_{i=1}^{n}\frac{n}{gcd(i,n)}=\sum_{i=1}^{n}gcd(i,n)

h(n)=\sum_{i=1}^{n}\sum_{d|gcd(i,n)}\phi (d)=\sum_{d|n}d\sum_{i=1}^{n/d}[gcd(i, n/d)==1]=\sum_{d|n}d\phi(d)

又有:g(n) = \sum_{m|n}m^{2}-\sum_{m|n}h(m),且h(n)与\sum_{m|n}h(m)都是积性函数。

对于n=p^{\alpha },推导可知\sum_{m|n}h(m)=(\alpha+1)p^{\alpha}那么推广就可有

\sum_{m|n}h(m)=n\prod_{i=1}^{k} (\alpha_{i}+1)

最终g(n) = \sum_{m|n}m^{2}-n\prod_{i=1}^{k} (\alpha_{i}+1)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef unsigned long long ll;

const int maxn = 32005;

int t, pri[maxn], num;
ll n, cur, ans, res;
int p[30], len, cnt[30];
bool flag[maxn];

void init()
{
    memset(flag, 0, sizeof(flag));
    num = 0;
    for(int i = 2;i < maxn;i++)
    {
        if(!flag[i])
        {
            pri[++num] = i;
            for(int j = 2*i;j < maxn;j += i)
                flag[j] = true;
        }
    }
}

int main()
{
    init();
    scanf("%d", &t);
    while(t--)
    {
        scanf("%llu", &n);
        ans = 1, res = n;
        len = 0;
        memset(cnt, 0, sizeof(cnt));
        for(int i = 1;i <= num;i++)
        {
            if(pri[i]*pri[i] > n) break;
            if(n % pri[i] == 0) p[len++] = pri[i];
            while(n % pri[i] == 0)
            {
                cnt[len-1]++;
                n /= pri[i];
            }
        }
        if(n > 1) {p[len] = n; cnt[len++] = 1;}
        for(int i = 0;i < len;i++)
        {
            res *= (cnt[i]+1);
            cur = 1;
            for(int j = 1;j <= cnt[i];j++)
                cur = cur*p[i]*p[i] + 1;
            ans *= cur;
        }
        ans -= res;
        printf("%llu\n", ans);
    }
    return 0;
}

E - Rebuild

给出一个多边形,要求以每个顶点为圆心作圆,使得每条边的两个顶点的圆的半径之和为这条边的长度。问是否存在合法方案,若存在则求出圆的总面积的最小值。

可以发现,如果n是奇数,那么如果有解则必定只存在一组解。而n是偶数时,如果有解则可行解是一个区间。

先判断是否有解, 对于n条边可以得到n个不等式,解这个不等式组,如果能得到合法的解区间则有解。在有解的情况下,如果n为奇数则只需要求出唯一解,如果n是偶数则需要在解区间上三分求出总面积最小的点。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn = 10050;
const double eps = 1e-8;
const double pi = acos(-1.0);

int t, n;
double res[maxn];
struct node
{
    double x, y;
    double len;
}e[maxn];
int sgn(double x) {return fabs(x) < eps ? 0 : (x < 0 ? -1 : 1);}
double dis(node a, node b)
{
    return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
bool Equal(double a, double b) {return sgn(a - b) == 0;}

double calArea(double x)
{
    double res = x*x, cur = x;
    for(int i = 0;i < n-1;i++)
    {
        cur = e[i].len - cur;
        res += cur*cur;
    }
    return res*pi;
}

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for(int i = 0;i < n;i++)
            scanf("%lf%lf", &e[i].x, &e[i].y);
        for(int i = 0;i < n;i++)
            e[i].len = dis(e[i], e[(i+1)%n]);
        res[0] = 0;
        for(int i = 0;i < n;i++)
        {
            if(i & 1) res[0] -= e[i].len;
            else res[0] += e[i].len;
        }
        if(n & 1)
        {
            res[0] /= 2;
            for(int i = 1;i < n;i++)
                res[i] = e[i-1].len - res[i-1];
            if(!Equal(e[n-1].len - res[n-1], res[0])) puts("IMPOSSIBLE");
            else
            {
                bool vis = 0;
                for(int i = 0;i < n;i++)
                    if(sgn(res[i]) < 0) {vis = 1; break;}
                if(vis) {puts("IMPOSSIBLE"); continue;}
                double ans = 0;
                for(int i = 0;i < n;i++) ans += (res[i]*res[i]);
                printf("%.2lf\n", ans*pi);
                for(int i = 0;i < n;i++) printf("%.2lf\n", res[i]);
            }
        }
        else
        {
            if(sgn(res[0]) != 0) {puts("IMPOSSIBLE"); continue;}
            double L = 0, R = e[0].len, cur = e[0].len, mid, mmid;
            for(int i = 1;i < n;i++)
            {
                if(i & 1) {cur -= e[i].len; L = max(L, cur);}
                else {cur += e[i].len; R = min(R, cur);}
            }
            if(L > R) {puts("IMPOSSIBLE"); continue;}
            for(int i = 0;i < 120;i++)
            {
                mid = (L + R)/2;
                mmid = (mid + R)/2;
                double sa = calArea(mid), sb = calArea(mmid);
                if(sa - sb > eps) L = mid;
                else R = mmid;
            }
            res[0] = L;
            bool flag = 0;
            for(int i = 0;i < n - 1;i++)
            {
                res[i+1] = e[i].len - res[i];
                if(sgn(res[i+1]) < 0) flag = 1;
            }
            if(flag) puts("IMPOSSIBLE");
            else
            {
                printf("%.2lf\n", calArea(L));
                for(int i = 0;i < n;i++)
                    printf("%.2lf\n", res[i]);
            }
        }
    }
    return 0;
}

F - Almost Sorted Array

给出一个数列,最多去掉一个数字,问能否将其变成单调不上升或单调不下降。

签到题,只要最长不上升/不下降子序列的长度不小于n-1即可。

G - Dancing Stars on Me

给出n个整数点的坐标,问这些点是否为正n边形的n个顶点。

考虑到给出的坐标都为整数,那么当且仅当n=4时有解。

判断正方形只需要先判四条边是否相等,再判对角线是否相等。遍历四个点的4!种排列。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;

const int maxn = 55;
const ll mod = 1e9 + 7;
const double eps = 1e-8;

int t, n;
struct node
{
    ll x, y;
}e[105];

ll dis(node a, node b)
{
    return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
}

bool judge()
{
    int a[] = {0, 1, 2, 3};
    do
    {
        bool flag = 0;
        ll tmp = dis(e[a[0]], e[a[3]]);
        for(int i = 0;i < 3;i++)
        {
            if(tmp != dis(e[a[i]], e[a[i+1]]))
            {
                flag = 1;
                break;
            }
        }
        if(flag) continue;
        if(dis(e[a[0]], e[a[2]]) == dis(e[a[1]], e[a[3]])) return 1;
    }while(next_permutation(a, a + 4));
    return 0;
}

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for(int i = 0;i < n;i++)
            scanf("%lld%lld", &e[i].x, &e[i].y);
        if(n != 4) {puts("NO"); continue;}
        if(judge()) puts("YES");
        else puts("NO");
    }
    return 0;
}

H - Partial Tree

定义了树的权值为所有点的权值之和, 每个点的权值为f(d),其中d为这个点的度。求树的最大可能权值。

一个有n个点的树,所有点的权值之和为2n-2。显然每个点的度最少要为1,剩下的n-2个度可以自由分配到各个点上,因为只要总的度数为2n-2,一定存在一种结构使得这种分配方案是合法的。

用dp[i]表示分配了i个度数时最大总权值,那么dp[ i+j ] = max{dp[ i ] + f(j+1) - f(1)}。其中初始化dp[0] = n*f(1),答案即为dp[n-2]。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;

const int maxn = 2505;
const ll mod = 1e9 + 7;
const ll INF = (1LL << 62) - 1;
const double eps = 1e-8;

int t, n;
ll f[maxn], dp[maxn];

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for(int i = 1;i < n;i++)
            scanf("%lld", &f[i]);
        for(int i = 0;i <= n;i++)dp[i] = -INF;
        dp[0] = n*f[1];
        for(int i = 0;i <= n-2;i++)
        {
            for(int j = 1;i + j <= n-2;j++)
                dp[i+j] = max(dp[i+j], dp[i] + f[j+1] - f[1]);
        }
        printf("%lld\n", dp[n-2]);
    }
    return 0;
}

J - Chip Factory

给出一个数列a,要求找出其中的三项ai,aj,ak,使得(a_{i}+a_{j})\bigoplus a_{k}最大,求这个最大值。

考虑在O(n^3)的暴力的基础上进行优化,我们可以O(n^2)地求出两项之和,然后在剩余的部分里找出一个异或值最大的。考虑到使异或最大是从高位到低位依次计算,这个过程就可以用字典树来实现。每次将需要相加的两个数从字典树删去,计算完成后再将它们插入进去。总的时间复杂度为O(n^{2}logn)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e4+10;
const ll INF = (1LL << 62) - 1;
const int len = 32;
const double eps = 1e-8;

ll a[maxn];
struct Trie{
	int root, tot, nex[maxn][2], num[maxn];
	ll End[maxn];
	inline int newnode(){
		memset(nex[tot], -1, sizeof(nex[tot]));
		End[tot] = 0;
		num[tot] = 0;
		return tot++;
	}
	inline void init(){
		tot = 0;
		root = newnode();
	}
	inline void Insert(ll x){
		int p = root;
		for(int i = 32; i >= 0; i--){
			int idx = ((1LL<<i)&x)? 1: 0;
			if(nex[p][idx] == -1)nex[p][idx] = newnode();
			p = nex[p][idx];
			num[p]++;
		}
		End[p] = x;
	}
	inline update(ll x, int d){
		int cur = root;
		for(int i = 32; i >= 0; i--){
			int index = (x&(1LL<<i))? 1: 0;
			cur = nex[cur][index];
			num[cur] += d;
		}
	}
	inline ll Search(ll x){
		int p = root;
		for(int i = 32; i >= 0; i--){
			int idx = ((1LL<<i)&x)? 1: 0;
			if(nex[p][idx^1] && num[nex[p][idx^1]])p = nex[p][idx^1];
			else p = nex[p][idx];
		}
		return x^End[p];
	}
}tr;
int main(){
    int T;
    scanf("%d", &T);
    while(T--){
		int n;
		scanf("%d", &n);
		tr.init();
		for(int i = 0; i < n; i++){
			scanf("%lld", &a[i]);
			tr.Insert(a[i]);
		}
		ll ans = 0;
		for(int i = 0; i < n; i++){
			for(int j = i+1; j < n; j++){
				tr.update(a[i], -1);
				tr.update(a[j], -1);
				ans = max(ans, tr.Search(a[i]+a[j]));
				tr.update(a[i], 1);
				tr.update(a[j], 1);
			}
		}
		printf("%lld\n", ans);
    }
    return 0;
}

L - House Building

若干个1*1木块放在n*m格子中,给出摆放的情况,求除底面以外的表面积。

签到题,每一行和每一列扫一遍,将相邻两个位置高度差求和,再加上俯视面积即可。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;

const int maxn = 55;
const ll mod = 1e9 + 7;

int t, n, m;
ll a[maxn][maxn];

ll Abs(ll a) {return a < 0 ? -a : a;}

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= m;j++)
                scanf("%lld", &a[i][j]);
        }
        ll ans = 0;
        for(int i = 1;i <= n;i++)
        {
            ll now = 0;
            for(int j = 1;j <= m;j++)
            {
                ans += Abs(now - a[i][j]);
                now = a[i][j];
            }
            ans += now;
        }
       for(int j = 1;j <= m;j++)
        {
            ll now = 0;
            for(int i = 1;i <= n;i++)
            {
                ans += Abs(now - a[i][j]);
                now = a[i][j];
            }
            ans += now;
        }
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= m;j++)
            {
                if(a[i][j] != 0)
                    ans++;
            }
        }
        printf("%lld\n" ,ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/NPU_SXY/article/details/82919726
今日推荐