I、 Intergalactic Bidding
theme:n个人在进行拍卖,每个人的出价至少是当前最高价的两倍,给定s,输出出价和为s的这些人的名字,没有则输出0
solution: 先将这n个人按钱数从大到小排,先找到钱数开始<=s的人,由于“每个人的出价至少是当前最高价的两倍”,可知对于任意一个人i,他的钱比他后面人(钱数小于他)的钱数之和还大。所以要想和达到s,则这个人必须选(因为剩下的加起来都没i钱多,那就更<s了),所以遍历后s-=m[i],之后重复寻找。最终s=0代表有,!=0则表示不存在组合。
由于s的是z大整数,所以考虑用java写,注意删掉包名,用Main命名类
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
import javax.naming.ldap.SortControl;
class Participnt{
public String name;
public BigInteger t;
public Participnt(String name,BigInteger t){
this.name=name;
this.t=t;
}
}
public class Main {
public static void main(String args[]){
Scanner input=new Scanner(System.in);
int n =input.nextInt();
BigInteger s=input.nextBigInteger();
Participnt participnt[] = new Participnt[n];
String ans[]=new String[n];
int ansnum=0;
for(int i=0;i<n;++i){
String name=input.next();
BigInteger t=input.nextBigInteger();
participnt[i]=new Participnt(name, t);
}
Arrays.sort(participnt,0,n,new MyCompare());
for(int i=n-1;i>=0;--i){
if(s.compareTo(participnt[i].t)>=0){
s=s.subtract(participnt[i].t);
ans[ansnum++]=new String(participnt[i].name);
}
}
Arrays.sort(ans,0,ansnum);
if(s.compareTo(BigInteger.ZERO)==0){
System.out.println(ansnum);
for(int i=0;i<ansnum;++i){
System.out.println(ans[i]);
}
}else
System.out.println(0);
}
static class MyCompare implements Comparator<Participnt>{
@Override
public int compare(Participnt a, Participnt b) {
if(a.t.compareTo(b.t)>0){
return 1;
}else if(a.t.compareTo(b.t)<0){
return -1;
}else
return 0;
}
}
}
K、 King's Colors
theme:给定一颗n个结点的树, 结点编号从0~n,有k种颜色,问怎么用这k种颜色给n个结点涂色,使得k种颜色都被用上且相邻结点(父子结点,注意不是相邻兄弟结点)颜色不同,不同方案数有几种?
solution:如果是最多用k种颜色,则答案是个G(k)=k(k-1)^(n-1)种,设f(k)为恰好用k种颜色的方案数,则
由二项式反演得
extension:二项式反演
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
#define far(i,a,b) for(int i=a;i<b;++i)
#define fdr(i,a,b) for(int i=b-1;i>=a;--i)
int mod=1000000007;
ll c[2505][2505]={0};
void getC(int x)
{
far(i,0,x+1)
{
c[i][0]=1;
c[i][i+1]=0;
for(int j=1;j<=i;++j)
{
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
//cout<<i<<" "<<j<< " "<<c[i-1][j]<<" "<<c[i-1][j-1]<<" "<<c[i][j]<<endl;
}
}
}
ll qm(ll x,int n)//x^n
{
ll ans=1;
while(n)
{
if(n%2)
ans=ans*x%mod;
x=x*x%mod;
n>>=1;
}
return ans;
}
int main()
{
getC(2500);
int n,k;
cin>>n>>k;
int x;
far(i,1,n)
scanf("%d",&x);
ll sum=0;
far(i,2,k+1)
{
int sign=(k-i)%2 ?-1:1;
sum=((sum+sign*c[k][i]*i%mod*qm((ll)i-1,n-1)%mod)%mod+mod)%mod;
}
printf("%lld",sum);
}
- 代码中注意的点:组合求法采用dp,c[i][j]=c[i-1][j]+c[i-1][j-1]
- (k-1)^(n-1)用快速幂
- 注意结果为正,而sum在一步步加的过程中可能为负,(a-b)%5与a%5-b%5不等!
F、Firing the Phaser
theme:给定r个边界与x,y轴平行的矩形房间,由左下角和右上角坐标表示,有一艘飞船想通过发射一束激光来一次性袭击尽可能多的房间,激光的起点和发射角度自定,但长度l已知,问最多能一次袭击多少个房间(交叉就代表被袭击)
A、Altruistic Amphibians
theme:有n只青蛙掉进深度为d的洞里,给定这n只青蛙能跳的高度l,重量w和高度h,每次青蛙可以通过叠在比它重的青蛙的背上跳出,这个时候它能跳的高度就是h1+l2了,可以无限叠但要保证每只青蛙的重量要>=它上面所有青蛙的重量之和。叠完后一只跳出后下面的青蛙重回到地面重新叠。问最多有多少只青蛙能跳出去(>d才能跳出)。
solution:按重量从大到小排序(因为重量最大的只能靠自己,没法踩在别的青蛙身上),然后遍历每只青蛙,前面的都是比他重的,此时的目的是找到满足重量叠加条件且叠起高度最高的叠加方案,用dp[w[i]]表示重量为w[i]的青蛙能叠起来的最大高度,则状态转移方程为dp[j]=max(dp[j],h[i]+dp[w[i]+j])(for j in range[1,wi-1]),因为如果重量为w[i]的青蛙都能叠上去,则重量比它轻的也一定能叠在相同的青蛙上。(即看它正叠在哪只青蛙上最高)
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
#define far(i,n) for(int i=0;i<n;++i)
struct _a
{
int l,w,h;
}a[100010];
ll dp[100000010]={0};
int m=100000001;
bool cmp(_a x,_a y)
{
return x.w>y.w;
}
int main()
{
int n,d;
cin>>n>>d;
far(i,n)
scanf("%d%d%d",&a[i].l,&a[i].w,&a[i].h);
sort(a,a+n,cmp);
int ans=0;
far(i,n)
{
if(dp[a[i].w]+a[i].l>d)
++ans;
for(int j=a[i].w+1;j<min(a[i].w*2,m);++j)
dp[j-a[i].w]=max(dp[j-a[i].w],dp[j]+a[i].h);
}
printf("%d",ans);
}
H、House Lawn
theme: