====== BUAA ICPC 2020-2021 ======
[[https://ac.nowcoder.com/acm/contest/5669|比赛网址]]
===== 训练结果 =====
* 时间:''2020.7.23 12:00~17:00''
* rank:''7/17''
* 完成情况:''5/6/11''
===== 题解 =====
===== F.Empty Vessels =====
=== 题意 ===
给了 n 个水杯,可以把水杯装满水或倒完,也可以向另外的杯子倒直至一个杯满或空,问你能否倒出指定体积的水。
=== 题解 ===
首先最大的被子都装不下就不可能实现。我们把最大的杯子当成容器,然后让别的杯子往里倒水,这就变成了一个模意义下的完全背包问题。
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
int read()
{
int k=0,f=1;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) k=k*10+c-'0';return k*f;
}
const int N=20055;
int n,A,a[N],f[N],pre[N],P,pos;
queue q;
void print(int x,int cnt)
{
if(!pre[x]) {printf("%d\n",cnt);return;}
if(a[pre[x]]<=x)
{
print(x-a[pre[x]],cnt+2);
printf("1 %d\n",pre[x]);
printf("3 %d %d\n",pre[x],pos);
}
else
{
print(x-a[pre[x]]+P,cnt+4);
printf("1 %d\n",pre[x]);
printf("3 %d %d\n",pre[x],pos);
printf("2 %d\n",pos);
printf("3 %d %d\n",pre[x],pos);
}
}
int main()
{
n=read();A=read();
f[0]=1;
for(int i=1;i<=n;i++)
{
a[i]=read();
if(a[i]>P)
P=a[i],pos=i;
}
if(A==P)
{
puts("1");
printf("1 %d\n",pos);
return 0;
}
q.push(0);
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=1;i<=n;i++)
if(!f[(u+a[i])%P])
f[(u+a[i])%P]=1,pre[(u+a[i])%P]=i,q.push((u+a[i])%P);
}
if(!f[A]) puts("-1");
else print(A,0);
return 0;
}
===== G.Maximum Product =====
=== 题意 ===
求区间里一个数,使数的每一位的乘积最大。
=== 题解 ===
答案一定为最高几位和右区间的相同,后面就是某一位减一之后全是九,枚举这一位出现的位置求出最大乘积的值。
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
int read()
{
int k=0,f=1;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) k=k*10+c-'0';return k*f;
}
ll n,m,sum=1,a[20],b[20],ans[105][105],vis[20];
int main()
{
scanf("%lld%lld",&n,&m);
while(n) a[++a[0]]=n%10,n/=10;
while(m) b[++b[0]]=m%10,m/=10;
int fl=0,fl2=0;
for(int i=b[0];i;i--)
{
if(!fl)
{
if(a[i]==b[i]) ans[1][i]=a[i];
else
{
fl=1;
ans[1][i]=b[i],vis[i]=1;
}
}
else ans[1][i]=b[i],vis[i]=1;
}
for(int i=b[0];i;i--)
{
if(vis[i])
{
if(b[i]=0) break;
sum++;
for(int j=b[0];j>i;j--)
ans[sum][j]=b[j];
ans[sum][i]=b[i]-1;
for(int j=i-1;j;j--)
ans[sum][j]=9;
}
}
// cout<res) res=as,pos=i;
}
int st=b[0];
while(ans[pos][st]==0) st--;
for(;st;st--)
printf("%d",ans[pos][st]);
return 0;
}
===== H.Biathlon 2.0 =====
=== 题意 ===
有$n$组$(a,b)$,$m$组$(c,d)$,对于每一个组$(a_i,b_i)$,选出一个$(c_j,d_j)$,使得$a_i*c_j+b_i*d_j$最小。
=== 题解 ===
首先按$c_i$升序,$d_i$降序排序,把一些没意义的组删去,接下来推一波式子得到$\frac{a_i}{b_i}>\frac{d_k-d_j}{c_j-c_k}$。这个式子的意思是,如果$j,k$满足这个式子,那么对于第$i$组,选$j$就比$k$更优,于是我们对第二类维护一个下凸壳,按照$\frac{a_i}{b_i}$降序排列,然后一个个扫描,取最优值即可。
#include
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair PII;
#define X first
#define Y second
inline int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int maxn=500010;
struct Item
{
LL x,y;
int id;
Item() {}
Item(LL _1,LL _2,int _3):x(_1),y(_2),id(_3) {}
}A[maxn],D[maxn],B[maxn],C[maxn],sta[maxn];
int n,m,M,len,a,b,top;
LL ans[maxn];
bool cmp1(Item a,Item b)
{
return (double)(a.x)/(double)(a.y)>(double)(b.x)/(double)(b.y);
}
bool cmp2(Item a,Item b)
{
return b.x==a.x ? a.yyj
int main()
{
n=read();
for(int i=1;i<=n;i++)a=read(),b=read(),A[i]=Item(a,b,i);
sort(A+1,A+n+1,cmp1);
m=read();
for(int i=1;i<=m;i++)a=read(),b=read(),B[i]=Item(a,b,i);
sort(B+1,B+m+1,cmp2);
for(int i=1;i<=m;)
{
C[++M]=B[i];
while(i<=m && B[i].y>=C[M].y)i++;
}
if(M==1)
{
for(int i=1;i<=n;i++)ans[A[i].id]=A[i].x*C[1].x+A[i].y*C[1].y;
}
else
{
sta[++top]=C[1];
sta[++top]=C[2];
for(int i=3;i<=M;i++)
{
while(top>1 && K(sta[top-1],sta[top])
===== I.Archaeological Research =====
=== 题意 ===
构造一个长度为$n$的数字序列(从数字1开始),使得满足一定的条件下字典序最小。
条件:对于每个位置$i$,给定之后的一些位置$p_j$,该位置$p_j$上的数字是$i$位置之后第一次出现
=== 题解 ===
要字典序最小,当然是使用当前能使用最小的数字
每次走到一个位置,如果这个位置没有被之前任意一个位置限制,那么就放$1$
否则,只需要查找最早限制的位置到这个位置之间最小的没被用过的数字
这个可以用可持久化线段树维护
建立一个权值线段树,记录每个权值最后出现的位置,维护区间最小值,对于区间$[l,r]$,只需要在$r$位置线段树中通过左右区间最小值判断区间内是否有合法权值,然后尽量往左走即可。
#include
#include
#include
#include
#include
#include
#include
#include
#include
===== J.Sockets =====
=== 题意 ===
有$n$个插排$m$个设备和一个插口,每个插排有各自的插口个数$a_i$,每个设备有各自的性能$b_j$,$b_j$表示该设备正常运行最多能经过的插排个数,求最多能使多少设备正常运行
=== 题解 ===
容易发现,插排一定是越大越要先用,设备一定是$b_j$越大越先用
那么就二分使用的设备个数,贪心地分层插入插排。如果当前层有必须使用的设备,就使用之,否则尽量插插排。最后看能否合法插入完。
#include
#include
#include
#include
#include
#include
#include
#include
#include
===== K.Toll Roads =====
=== 题意 ===
填坑
=== 题解 ===
填坑
===== 训练实况 =====
开局hxm看**A**,wxg看**G**,wxg开写**G**
fyh和hxm讨论**A**无果,fyh继续想**A**,hxm想**J**
12:38 wxg过**G**
hxm和wxg讨论出**J**做法
13:07hxm过**J**
fyh wxg想**H**,fyh开写**H**,中间目测调题,wxg尝试乱搞**A**
14:28 fyh过**H**,hxm想**I**,想出做法
hxm用错数据结构,和wxg讨论后修改
wxg wa **A** 放弃**A**
16:06 hxm过**I**,wxg想出**F**,fyh尝试**D**
16:40 wxg过**F**
=====训练总结=====
fyh:本次比赛我们队的罚时得到了很大的进步,大部分题都能1A,我在推H的时候不等号忘记变号,导致输不对答案,肉眼调了一小会,耽误了一点点时间,算是一个易错点。
\\ hxm:
\\ wxg:G题写的有点慢,A的乱搞没有搞过去。