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
定义:,求g(n)。
设:,那么。考虑化简h(n):
又有:,且h(n)与都是积性函数。
对于,推导可知那么推广就可有
最终
#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,使得最大,求这个最大值。
考虑在O(n^3)的暴力的基础上进行优化,我们可以O(n^2)地求出两项之和,然后在剩余的部分里找出一个异或值最大的。考虑到使异或最大是从高位到低位依次计算,这个过程就可以用字典树来实现。每次将需要相加的两个数从字典树删去,计算完成后再将它们插入进去。总的时间复杂度为。
#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;
}