國慶七天樂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
定義:,求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;
}