补题地址
榜单&补题情况
题解、分析和code一部分是自己写的,一部分是抄的队友的。
A. Altruistic Amphibians 01背包
题意
给出n只青蛙和洞的高度d,每只青蛙有l、w、h三个属性,分别表示跳跃高度、重量和身高。青蛙可以像叠罗汉一样叠在一起,但要求每只青蛙身上的重量小于它自身。当某只青蛙的跳跃高度+身下叠起来的高度>d时,青蛙可以跳出洞,问最多几只青蛙能跳出去?
分析
首先考虑如何安排青蛙跳出的顺序。最轻的青蛙无法提供任何高度,因此可以安排第一个跳出去,而重量最大的青蛙只能靠自己跳出去,所以一定是最后一个。于是结论是一个青蛙能否跳出去,只取决于重量比它大的青蛙的属性,因此可以将青蛙按照重量从大到小排序,依次决定是否能产生贡献。
此时很明显是一个01背包问题,只要用 d p [ i ] dp[i] dp[i]来表示重量为 i i i时,其他青蛙所能提供的最大高度就能做出来了。读题的时候感觉数据范围比较玄学就没继续往下想,忽略了题目中一个重要条件就是所有青蛙重量 w w w之和小于 1 e 8 1e8 1e8,那么对于第 i i i只青蛙,其重量为 w i w_i wi,只要修改 [ 1 , w i − 1 ] [1,w_i-1] [1,wi−1]范围内的 d p dp dp,就相当于考虑到借助第 i i i只青蛙后 d p dp dp数组的变化了。一个小优化是令 N = 1 e 8 N=1e8 N=1e8,那么只需要考虑 [ 1 , m i n ( w i − 1 , N − w i ) ] [1,min(w_i-1,N-w_i)] [1,min(wi−1,N−wi)]这个范围即可,因为除了 i i i之外剩下青蛙重量之和不超过 N − w i N-w_i N−wi,对于大于 N − w i N-w_i N−wi的情况就不需要记录其dp值了。
Code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e8 + 5, M = 1e5 + 5;
struct frog {
int l, w, h;
}a[M];
int n, d, dp[N], ans; //dp[i]表示重量为i时,其他青蛙所能提供的最大高度
int main() {
cin >> n >> d;
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &a[i].l, &a[i].w, &a[i].h);
}
sort(a + 1, a + 1 + n, [&](frog &u, frog &v){
return u.w > v.w;
});
int mx = a[1].w;
for (int i = 1; i <= n; i++) {
if (dp[a[i].w] + a[i].l > d) ans++;
for (int j = 1; j <= min(a[i].w - 1, N - a[i].w); j++) { //由于总重量之和不超过N,除了i之外剩下青蛙重量之和不超过N-a[i].w
dp[j] = max(dp[j], dp[a[i].w + j] + a[i].h); //判断重量为j时,借助第i个青蛙后能否更高
}
}
printf("%d\n", ans);
return 0;
}
B. Baby Bites 签到
Code
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
int n;
char s[100];
int getnum(char *s) {
int ans=0;
int len=strlen(s);
for (int i=0;i<len;i++) {
ans=ans*10+s[i]-'0';
}
return ans;
}
int main()
{
cin>>n;
bool a=1;
for (int i=1;i<=n;i++) {
scanf("%s",s);
if (strcmp(s,"mumble")==0) continue;
else {
if (getnum(s)!=i) a=0;
}
if (!a) break;
}
if (!a) printf("something is fishy\n");
else printf("makes sense\n");
return 0;
}
C. Code Cleanups 签到
Code
#include<bits/stdc++.h>
typedef long long LL;
#define lowbit(x) ((x)&-(x))
#define rep(x,y,z) for(int (x)=(y);(x)<=(z);(x)++)
#define per(x,y,z) for(int (x)=(y);(x)>=(z);(x)--)
using namespace std;
int n,a,b,c,d,x,y;
bool dir[1111];
int main()
{
cin>>n;
rep(i,1,n)
{
int x;
cin>>x;
dir[x]=true;
}
int dirt=0,cnt=0,ans=0;
rep(day,1,365)
{
dirt+=cnt;
if (dir[day]) cnt++;
if (dirt+cnt>=20)
{
cnt=0;dirt=0;ans++;
}
}
if (cnt) ans++;
cout<<ans<<endl;
return 0;
}
D. Delivery Delays 二分答案+DP
题意&分析
Code
#include <bits/stdc++.h>
#define debug(x) cerr << #x << ":" << x << endl
using namespace std;
typedef long long LL;
const int N = 1e3 + 5;
const LL INF = 0x3f3f3f3f3f3f3f3f;
LL n, m, k, s[N], u[N], t[N];
LL dis[N][N], dp[N], sumdis[N];
struct Dijkstra {
struct Edge {
int to; LL val;
};
struct Node {
LL dis; int key;
friend bool operator < (const Node &x, const Node &y) {
return x.dis > y.dis;
}
};
bool vis[N];
int n, s;
LL dis[N];
vector<Edge>G[N];
priority_queue<Node>Q;
void init(int _n, int _s) {
n = _n, s = _s;
for (int i = 1; i <= n; i++) dis[i] = INF;
for (int i = 1; i <= n; i++) vis[i] = 0;
}
void addEdge(int from, int to, LL val) {
G[from].push_back({to, val});
}
void go() {
dis[s] = 0;
Q.push({0,s});
while (!Q.empty()) {
Node top = Q.top(); Q.pop();
int now = top.key;
if (vis[now]) continue;
vis[now] = 1;
for (int i = 0; i < G[now].size(); i++) {
int to = G[now][i].to;
if (dis[to] > dis[now] + G[now][i].val) {
dis[to] = dis[now] + G[now][i].val;
if (!vis[to]) Q.push({dis[to], to});
}
}
}
}
}dj;
bool check(LL x) {
memset(dp, INF, sizeof(dp));
dp[0] = 0;
for (int i = 1; i <= k; i++) {
LL latest = INF;
for (int j = i - 1; j >= 0; j--) {
if (j == i - 1) latest = s[j + 1] + x - dis[1][u[j + 1]];
else latest = min(latest + dis[1][u[j + 2]] - dis[1][u[j + 1]] - dis[u[j + 1]][u[j + 2]], s[j + 1] + x - dis[1][u[j + 1]]);
if (latest < max(dp[j], t[i])) continue;
dp[i] = min(dp[i], max(dp[j], t[i]) + dis[1][u[j + 1]] + sumdis[i] - sumdis[j + 1]);
}
if (dp[i] > s[i] + x) return false;
dp[i] += dis[1][u[i]];
}
return true;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v, d;
scanf("%d %d %d", &u, &v, &d);
dj.addEdge(u, v, d);
dj.addEdge(v, u, d);
}
for (int i = 1; i <= n; i++) {
dj.init(n, i);
dj.go();
for (int j = i; j <= n; j++) dis[i][j] = dis[j][i] = dj.dis[j];
}
cin >> k;
u[0] = 1;
for (int i = 1; i <= k; i++) {
scanf("%d %d %d", &s[i], &u[i], &t[i]);
if (i == 1) continue;
sumdis[i] = sumdis[i - 1] + dis[u[i - 1]][u[i]];
}
LL l = 0, r = INF;
check(6);
while(l < r) {
LL mid = (l + r) >> 1;
if (check(mid)) {
r = mid;
} else l = mid + 1;
}
cout << l;
return 0;
}
E. Exploson Exploit 记忆化搜索
题意&分析
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef array<int,7> arr;
typedef pair<arr,arr> pii;
map<pii,double>dp;
arr st1,st2,ast1,ast2;
int n,m,d;
double dfs(arr st1,arr st2,int nzero,int p,int q) {
if (dp.find({st1,st2})!=dp.end()) return dp[{st1,st2}];
if (p==0) return 1;
if (q==0) return 0;
double res=0;
for (int i=1;i<=6;i++) {
for (int j=1;j<=st1[i];j++) {
ast1=st1,ast2=st2;
if (st1[i]==0) continue;
ast1[i]--;
ast1[i-1]++;
res+=dfs(ast1,ast2,nzero-(i==1),p,q-1)/nzero;
}
}
for (int i=1;i<=6;i++) {
for (int j=1;j<=st2[i];j++) {
ast1=st1,ast2=st2;
if (st2[i]==0) continue;
ast2[i]--;
ast2[i-1]++;
res+=dfs(ast1,ast2,nzero-(i==1),p-(i==1),q-1)/nzero;
}
}
return dp[{st1,st2}]=res;
}
int main() {
cin>>n>>m>>d;
for (int i=1;i<=n;i++) {
int x;
scanf("%d",&x);
st1[x]++;
}
for (int i=1;i<=m;i++) {
int x;
scanf("%d",&x);
st2[x]++;
}
cout.precision(20);
cout << dfs(st1,st2,n+m,m,d);
return 0;
}
H. House Lawn
一开始题意没读清,以为要选多个除草机,但其实只要选一个就行了。每个除草机运行以(t+r)周为周期,那么枚举0~t+r,保证每周平均下来都能完成一次就可以了。
Code
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=1e6+5,INF=0x3f3f3f3f;
int l,m,t,p,len;
char s[300];
struct mow{
string name;
int p,c,t,r,ord;
}a[N];
void readname(int now) {
string t;
while(s[p]!=',') {
t+=s[p];
p++;
}
a[now].name=t;
p++;
}
void readnum(int &now) {
int res=0;
while(s[p]!=',' && p<len) {
res=res*10+s[p]-'0';
p++;
}
now=res;
p++;
}
bool check(int now) {
bool res=1;
for (int i=1;i<=a[now].r+a[now].t;i++) {
LL time=1ll*i*10080;
LL worktime=time/(a[now].t+a[now].r)*a[now].t;
LL lfttime=time%(a[now].t+a[now].r);
worktime+=min(lfttime,1ll*a[now].t);
if (1ll*worktime*a[now].c>=1ll*l*i) res=1;
else res=0;
if (!res) break;
}
return res;
}
vector<string>ans;
int main()
{
cin>>l>>m;
getchar();
for (int i=1;i<=m;i++) {
gets(s);
len=strlen(s);
p=0;
readname(i);
readnum(a[i].p);
readnum(a[i].c);
readnum(a[i].t);
readnum(a[i].r);
a[i].ord=i;
}
sort(a+1,a+1+m,[&](mow a,mow b) {
if (a.p!=b.p)
return a.p>b.p;
else return a.ord<b.ord;
});
int mincost=INF;
for (int i=1;i<=m;i++) {
if (check(i)) {
if (a[i].p==mincost) {
ans.push_back(a[i].name);
}
else {
ans.clear();
ans.push_back(a[i].name);
mincost=a[i].p;
}
}
}
if (ans.size()==0) printf("no such mower");
else for (auto x:ans) cout<<x<<endl;
return 0;
}
I. Intergalactic Bidding 大数
因为后一个至少是前一个的两倍,所以手写大数,从大到小扫一遍能拿就拿。
Code
#include<bits/stdc++.h>
typedef long long LL;
#define lowbit(x) ((x)&-(x))
#define rep(x,y,z) for(int (x)=(y);(x)<=(z);(x)++)
#define per(x,y,z) for(int (x)=(y);(x)>=(z);(x)--)
using namespace std;
//template <typename T>
//void read(T &x){}
//template <typename T,typename U>
//void read(T &x,U &y){read(x);read(y);}
int n,tot;
char ans[2111][50];
struct BIGINT
{
int dig;//number of digits
int num[2111];
char name[50];
inline void read()
{
char inp[2111];
cin>>inp;
dig=strlen(inp);
per(i,dig-1,0)
{
num[dig-i]=inp[i]-'0';
}
}
bool operator<(const BIGINT &y)
{
if (dig<y.dig) return true;
else if (dig>y.dig) return false;
per(i,dig,1)
{
if (num[i]<y.num[i]) return true;
else if (num[i]>y.num[i]) return false;
}
return false;
}
void operator-=(const BIGINT &y)
{
bool flag=false;
rep(i,1,y.dig)
{
num[i]-=y.num[i];
if (flag) {num[i]--;flag=false;}
if (num[i]<0) {flag=true;num[i]=num[i]+10;}
}
if (flag) num[y.dig+1]--;
while (!num[dig]&&dig>0) dig--;
}
void output()
{
per(i,dig,1) cout<<num[i];
cout<<endl;
}
bool zero()
{
if (dig==0) return true;
return false;
}
bool eq(const BIGINT &y)
{
if (y.dig!=dig) return false;
rep(i,1,dig) if (y.num[i]!=num[i]) return false;
return true;
}
}a[2111],s;
int main()
{
cin>>n;
s.read();
rep(i,1,n)
{
cin>>a[i].name;
a[i].read();
}
sort(a+1,a+n+1);
per(i,n,1)
{
if (a[i]<s||a[i].eq(s))
{
strcpy(ans[++tot],a[i].name);
s-=a[i];
}
}
if (!s.zero())
{
cout<<0<<endl;
return 0;
}
cout<<tot<<endl;
rep(i,1,tot) cout<<ans[i]<<endl;
return 0;
}
J. Jumbled String 构造,分类
a=0时情况比较特殊,“1”的个数可能为1或者0,所以单独讨论一下个数为0的情况
Code
#include<bits/stdc++.h>
typedef long long LL;
#define lowbit(x) ((x)&-(x))
#define rep(x,y,z) for(int (x)=(y);(x)<=(z);(x)++)
#define per(x,y,z) for(int (x)=(y);(x)>=(z);(x)--)
using namespace std;
int n,a,b,c,d,x,y;
char s[100];
int check(int x) {
if (x==0) return 1;
x*=2;
int t=(1+sqrt(1+4ll*x))/2;
if (1ll*t*(t-1)==x) return t;
else return -1;
}
int main()
{
cin>>a>>b>>c>>d;
if (a==0 && d==0) {
if (b==1 && c==0) {
printf("01");
return 0;
}
if (b==0 && c==1) {
printf("10");
return 0;
}
if (b==0 && c==0) {
printf("0");
return 0;
}
printf("impossible");
return 0;
}
if (a==0) {
if (b==0 && c==0) {
y=check(d);
if (y==-1) {
printf("impossible");
return 0;
}
for (int i=1;i<=y;i++) printf("1");
return 0;
}
}
if (d==0) {
if (b==0 && c==0) {
x=check(a);
if (x==-1) {
printf("impossible");
return 0;
}
for (int i=1;i<=x;i++) printf("0");
return 0;
}
}
x=check(a);
y=check(d);
if (x==-1 || y==-1) {
printf("impossible");
return 0;
}
if ((b+c)!=(1ll*x*y)) {
printf("impossible");
return 0;
}
for (int i=1;i<=b/y;i++) printf("0");
for (int i=1;i<=y-b%y;i++) printf("1");
if (b%y!=0) printf("0");
for (int i=1;i<=b%y;i++) printf("1");
for (int i=1;i<=x-b/y-(b%y!=0);i++) printf("0");
return 0;
}
K. King’s Colors 组合数学
题意
一颗大小为n的树,要求用恰好k种颜色涂色,且相邻两个点颜色不同,问方案数。
分析
很容易得到 用小于等于k种颜色涂色的方案数为
k
(
k
−
1
)
n
−
1
k(k-1)^{n-1}
k(k−1)n−1 ,与树的形状是无关的
一开始想到用{小于等于k种颜色涂色}-{小于等于k-1种颜色涂色}就是答案了,但是发现后面一项还需要乘一个
(
k
k
−
1
)
\binom{k}{k-1}
(k−1k)(从k种颜色里选出k-1种),结果这一乘就出问题了。因为会使得用小于k-1种颜色涂色的情况被重复计算。(似乎是犯了一个非常初级的组合数学错误。。)
于是考虑到题目数据范围很小,完全可以从1开始,求出用恰好k种颜色涂色的方案数,再一项一项去掉即可。
Code
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int maxn = 3e3;
const int mod = 1e9+7;
vector<int> g[maxn];
int c[maxn][maxn];
void init()
{
c[0][0] = 1;
for(int i = 1; i < maxn; i++)
{
c[i][0] = 1;
for(int j = 1; j < maxn; j++)
{
c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
c[i][j] = c[i][j] % mod;
}
}
}
int ans[maxn];
int main()
{
init();
int n,k;
scanf("%d %d", &n, &k);
for(int i = 1; i < n; i++)
{
int x;
scanf("%d", &x);
g[x].push_back(i);
}
for(int i = 1; i <= k; i++)
{
int x = i;
for(int j = 1; j < n; j++)
x = 1ll * x * (i - 1) % mod;
for(int j = 1; j < i; j++)
x = ((x - 1ll * c[i][j] * ans[i - j] % mod) + mod) % mod;
ans[i] = x;
}
cout << ans[k] << endl;
return 0;
}