总分 255
跟LPL一起明年再战
T1 组合数问题
Description
组合数 C(n,m) 表示的是从 n 个物品中选出 m 个物品的方案数。 举个例子,从 (1, 2, 3) 三个物品中选择两个物品可以有 (1, 2), (1, 3), (2, 3) 这三种选择方法.。小葱想知道如果给定 n, m 和 k ,对于所有的 0 ≤ i ≤ n, 0 ≤ j ≤min(i,m)有多少对(i, j) 满足 C (i,j) 是 k 的倍数。
解题思路
久违的数学水题,虽然考试时由于电脑网速爆炸,导致心态爆炸,结果想出了一个伪正解,当时看到k好小,最大只有21,想都没想就用质因数分解预处理了,然后二维前缀和优化,也能A,不过比较复杂。
我的正解
struct PAC{
int num[5],cnt[5],tol,tmp[5];
int S[5][M],sum[M][M];
void Init(){
int s=k;
FOR(i,2,k){
if(s%i==0){
num[++tol]=i;
while(s%i==0){
cnt[tol]++;
s/=i;
}
}
}
FOR(i,2,2000){
FOR(j,1,tol){
int p=i;
while(p%num[j]==0){
p/=num[j];
S[j][i]++;
}
}
}
FOR(i,1,2000){
FOR(j,1,tol)tmp[j]=0;
int a=i,b=1;
FOR(j,1,i){
int flag=1;
FOR(h,1,tol){
tmp[h]+=S[h][a]-S[h][b];
if(tmp[h]<cnt[h])flag=0;
}
a--;b++;
if(flag)sum[i][j]=1;
}
FOR(j,1,2000)sum[i][j]=sum[i][j-1]+sum[i][j];
FOR(j,1,2000){
sum[i][j]+=sum[i-1][j];
}
}
}
void solve(){
Init();
int n,m;
while(t--){
scanf("%d%d",&n,&m);
printf("%d\n",sum[n][min(n,m)]);
}
}
}pac;
其实直接模k就好了,因为对于一个f(x)=k*a,f(x)mod(k)=0。
手打标程
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a),i_##END_=(b);i<=i_##END_;++i)
#define REP(i,a,b) for(int i=(a),i_##BEGIN_=(b);i>=i_##BEGIN_;--i)
#define M 2005
int t,k;
struct PAC{
int C[M][M],sum[M][M];
void Init(){
FOR(i,1,2000){
C[i][0]=C[i][i]=1%k;
FOR(j,0,i){
if(j!=0&&j!=i)C[i][j]=(C[i-1][j]+C[i-1][j-1])%k;
if(!C[i][j])sum[i][j]=1;
}
FOR(j,1,2000)sum[i][j]=sum[i][j-1]+sum[i][j];
FOR(j,1,2000){
sum[i][j]+=sum[i-1][j];
}
}
}
void solve(){
Init();
int n,m;
while(t--){
scanf("%d%d",&n,&m);
printf("%d\n",sum[n][min(n,m)]);
}
}
}pac;
int main(){
cin>>t>>k;
pac.solve();
return 0;
}
T2 蚯蚓
Description
本题中,我们将用符号 LcJ 表示对 c 向下取整,例如: L3.0J = L3.1J = L3.9J= 3 。蛐蛐国最近虹蚓成灾了! 隔壁跳蚤国的跳蚤也拿虹蚓们没办法,蛐蛐国王只好去请神刀子来帮他们消灭虹蚓。蛐蛐国里现在共有 n 只虹蚓( n 为正整数)。 每只虹蚓拥有长度,我们设第 i 只虹 蚓的长度为 ai ( i = 1, 2, … , n ),并保证所有的长度都是非负整数(即:可能存在长度为 0 的虹蚓)。
每一秒,神刀子会在所有的虹蚓中,准确地找到最长的那一只(如有多个则任边 一个)将其切成两半。 神刀子切开虹蚓的位置由常数 p (是满足 0 < p < 1 的有理数) 决定,设这只虹蚓长度为 x ,神刀子会将其切成两只长度分别为 LpxJ 和 x − LpxJ 的虹蚓。特殊地,如果这两个数的其中一个等于 0 ,则这个长度为 0 的虹蚓也会被保留。此外,除了刚刚产生的两只新虹蚓,其余虹蚓的长度都会增加 q (是一个非负整常数)。蛐蛐国王知道这样不是长久之计,因为虹蚓不仅会越来越多,还会越来越长。蛐蛐国王决定求助于一位有着洪荒之力的神秘人物,但是救兵还需要m 秒才能到来… …( m 为非负整数)
蛐蛐国王希望知道这 m 秒内的战况。 具体来说,他希望知道:
• m 秒内,每一秒被切断的虹蚓被切断前的长度(有 m 个数);
• m 秒后,所有虹蚓的长度(有 n + m 个数)。
蛐蛐国王当然知道怎么做啦! 但是他想考考你… …
这个pdf复制下来十分鬼畜,我知道是蚯蚓啊,蚯蚓。
解题思路
刚开始以为只能拿个35模拟分,考试最后20分钟想出70分做法。模拟就不了,反复sort就行了。
q=0时,可以发现所有蚯蚓最后的长度小于等于之前的长度,所以先砍的肯定长一点,并且我们可以发现每次砍下来的两段蚯蚓都是具有单调性的,所以我们把每次砍下来的蚯蚓装进数组,然后三个数组(包含原数组)内的元素都具有单调性,然后用归并处理下,得到最后的答案。
最后20分钟敲得,最后5分钟D的bug,可能有一点问题,望大佬指出。
q=0的代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a),i_##END_=(b);i<=i_##END_;++i)
#define REP(i,a,b) for(int i=(a),i_##BEGIN_=(b);i>=i_##BEGIN_;--i)
#define M 7000005
int n,m,q,u,v,t;
int A[100005];
int cmp(int a,int b){return a>b;}
struct P70{
int ans[M],cnt,tol,gg;
int B[M],C[M],Bag[M+100005],Bag2[M+100005];
void solve(){
sort(A+1,A+n+1,cmp);
int a=1,res1=0,res2=0;
int ed1=0,ed2=0;
FOR(i,1,m){
int mx=max(A[a],max(B[ed1],C[ed2]));
if(i%t==0)ans[++cnt]=mx;
if(mx==B[ed1]){
long long tmp=1ll*u*B[ed1];
tmp/=v;long long s=B[ed1]-tmp;
ed1++;
B[res1++]=tmp;C[res2++]=s;
}
else if(mx==C[ed2]){
long long tmp=1ll*u*C[ed2];
tmp/=v;long long s=C[ed2]-tmp;
ed2++;
B[res1++]=tmp;C[res2++]=s;
}
else if(mx==A[a]){
long long tmp=1ll*u*A[a];
tmp/=v;long long s=A[a]-tmp;
a++;
B[res1++]=tmp,C[res2++]=s;
}
}
int i=a,j=ed1;
while(i<=n&&j<=res1){
if(A[i]>B[j])Bag[++tol]=A[i++];
else Bag[++tol]=B[j++];
}
while(i<=n)Bag[++tol]=A[i++];
while(j<=res1)Bag[++tol]=B[j++];
i=1,j=ed2;
while(i<=tol&&j<=res2){
if(Bag[i]>C[j])Bag2[++gg]=Bag[i++];
else Bag2[++gg]=C[j++];
}
while(i<=tol)Bag2[++gg]=Bag[i++];
while(j<=res2)Bag2[++gg]=C[j++];
FOR(i,1,cnt)printf("%d ",ans[i]);
puts("");
FOR(i,1,n+m){
if(i%t==0)printf("%d ",Bag2[i]);
}
puts("");
}
}p70;
int main(){
cin>>n>>m>>q>>u>>v>>t;
FOR(i,1,n)scanf("%d",&A[i]);
p70.solve();
return 0;
}
正解
T2正解其实跟q=0差不多,因为长度长的蚯蚓被砍下来的两段分别比短的对应的两段长,其实可以用模拟理解。
比如常数p=0.5,q=2。有一只长度4的蚯蚓和一只长度三的蚯蚓
原序列排序后 4 3
第一次切断后 5 2 2//被切的长度不会增加
第二次切断后 4 4 3 2
可以发现,就算长度会增加,单调性还是不变的,因为每只蚯蚓被切断的那一轮长度都不会增加,所以这一点是平等的,所以单调性依然不变。
正解代码我抄的,不过我真的看懂了
struct PAC {
int Q1[M],Q2[M],Q3[M],Q4[M+100005];
int l1,l2,l3,r1,r2,r3,r4;
int Calc(int x) {
int s1=x*q+Q1[l1],s2=(x-l2-1)*q+Q2[l2],s3=(x-l3-1)*q+Q3[l3];
if(l1<r1&&s1>s2&&s1>s3) {
l1++;
return s1;
} else if(l2<r2&&s2>s3) {
l2++;
return s2;
} else if(l3<r3) {
l3++;
return s3;
}
return -1;
}
void solve() {
sort(A+1,A+n+1,cmp);
FOR(i,1,n)Q1[i-1]=A[i];
r1=n;
FOR(i,1,m) {
int a=Calc(i-1);
if(i%t==0)printf("%d ",a);
long long tmp=1ll*a*u/v;
long long s=a-tmp;
Q2[r2++]=tmp;
Q3[r3++]=s;
}
puts("");
r4=0;
while(Q4[r4]=Calc(m),Q4[r4]!=-1)r4++;
for(int i=t-1;i<n+m;i+=t)printf("%d ",Q4[i]);
}
} pac;
T3 愤怒的小鸟
Description
Kiana最近沉迷于一款神奇的游戏无法自拔。
简单来说,这款游戏是在一个平面上进行的。
有一架弹弓位于 (0, 0) 处,每次Kiana可以用它向第一象限发射一只红色的小鸟, 小鸟们的飞行轨迹均为形如 y = ax^2 + bx 的曲线,其中 a, b 是Kiana指定的参数,且必须满足 a < 0 。当小鸟落回地面(即 x 轴)时,它就会瞬间消失。
在游戏的某个关卡里,平面的第一象限中有 n 只绿色的小猪,其中第 i 只小猪所在的坐标为 (xi, yi) 。如果某只小鸟的飞行轨迹经过了 (xi, yi) ,那么第 i 只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;如果一只小鸟的飞行轨迹没有经过 (xi, yi) ,那么这只小鸟飞行的全过程就不会对 第 i 只小猪产生任何影响。例如,若两只小猪分别位于 (1, 3) 和 (3, 3) ,Kiana可以选择发射一只飞行轨迹为y = −x^2 + 4x 的小鸟,这样两只小猪就会被这只小鸟一起消灭。 而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。这款神奇游戏的每个关卡对Kiana来说都很难,所以Kiana还输入了一些神秘的指令,使得自己能更轻松地完成这个游戏。 这些指令将在【输入格式】中详述。假设这款游戏一共有 T 个关卡,现在Kiana想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。 由于她不会算,所以希望由你告诉她。
解题思路
刚开始只想拿暴力分,结果我瞄了一眼数据,n<=18,我当时的表情大概是这样的。
这#@#$*(哔~~~)不是裸的状压吗,暴力,不存在的。
我们可以发现,这个发射的抛物线要么只打一只小鸟,要么瞄着两只小鸟打,一只小鸟很简单,直接把状态加进去就好了,两只小鸟就要算出这两只小鸟呈的抛物线,注意精度误差就好了。
对于每个状压位,1表示这只猪还活着,0表示被打死了。对于每个状态,把每个抛物线带进去算一下就好了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a),i_##END_=(b);i<=i_##END_;++i)
#define REP(i,a,b) for(int i=(a),i_##BEGIN_=(b);i>=i_##BEGIN_;--i)
#define M 20
#define e 1e-6
typedef double db;
int T,n,m;
struct node{
db x,y;
}P[M];
int tol,Sit[M*M];
int dp[1<<M];
void Init(){
FOR(i,1,n)FOR(j,i+1,n){
if(P[i].x==P[j].x)continue;
db tmp1=P[i].x*P[i].x,res1=P[i].x;
db tmp2=P[j].x*P[j].x,res2=P[j].x;
db aa=res2/res1;
db a=(P[i].y*aa-P[j].y)/(tmp1*aa-tmp2);
db b=(P[i].y-a*tmp1)/res1;
tol++;
if(a>=0)continue;
Sit[tol]|=1<<(i-1);
Sit[tol]|=1<<(j-1);
FOR(k,1,n){
if(k==i||k==j)continue;
if(abs(P[k].x*P[k].x*a+P[k].x*b-P[k].y)<=e)Sit[tol]|=1<<(k-1);
}
}
}
int cmp(node a,node b){
return a.x<b.y;
}
int main(){
cin>>T;
while(T--){
tol=0;
scanf("%d%d",&n,&m);
FOR(i,1,n)scanf("%lf%lf",&P[i].x,&P[i].y);
memset(Sit,0,sizeof(Sit));
FOR(i,1,n)Sit[++tol]=1<<(i-1);
Init();
FOR(i,0,(1<<n)-1)dp[i]=1e9;
dp[(1<<n)-1]=0;
REP(i,(1<<n)-1,0){
FOR(j,1,tol){
dp[i]=min(dp[i],dp[i|Sit[j]]+1);
}
}
printf("%d\n",dp[0]);
}
return 0;
}