NKOJ2019四月月赛
这场比赛打得是心态爆炸。该拿的分没拿完。哎,自己是真的菜。
A.切火腿肠( )
何老板有N根大小相同且质地均匀的火腿肠,要分给M名信竞队员。要求每名队员分得的香肠重量相同。
何老板想知道,最少切多少刀就能满足上述要求?
一行,两个整数N和M,( )
解:
假设每根火腿肠长度为
,则总长度为
所以每个人能够分到的长度为:
一根长度为S的火腿需要切:
此时考虑某一个切点恰好是位于两根火腿之间的
设最少第
刀在两根火腿之间,则:
注:这题规模很小,可以直接暴力
#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
int main()
{
int n,m;
cin>>n>>m;
cout<<m-gcd(n,m);
return 0;
}
B.翻硬币( )
何老板将
个硬币排成一排。从左往右编号
到
。有的正面(国徽)朝上,有的背面(面额)朝上。
为方便表示,何老板用字母
代表正面朝上,
代表背面朝上。于是就得到一个由大写字母
和
构成的长度为
的字符串
。
对任意相邻的两个硬币 和 号硬币 。若满足 是正面朝上, 是背面朝上,你就可以对它们进行翻转操作,翻转后 的背面朝上, 的正面朝上。
何老板想知道,最多能进行多少次上述翻转操作?
解
其实我们可以贪心一下,如果要进行操作的次数最多,则最好把所有的 都移到右边,那么对于每一个 ,则它会被在它左边的每一个 都交换一次,所以答案就很显然了
#include<bits/stdc++.h>
using namespace std;
int main()
{
char c;
long long ans=0,x=0;
while((c=getchar())!=EOF)
{
if(c=='W')ans+=x;
else x++;
}
cout<<ans;
return 0;
}
C.三分数组
给出一个有
个整数的数组
, 有多少种方法把数组分成
个连续的子序列,使得各子序列的元素之和相等。也就是说,有多少个下标对
满足:
第
行:
个整数
接下来n 行,每行1 个整数,表示
解
首先先前缀和处理,然后再判断
,若不整除,则无解。如果整除:
定义
考虑:
对于每一个
的位置,它能组成的下标对的个数就是在它前面的
的个数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
ll a[maxn],sum[maxn],ans;
ll n;
inline void rin(ll &t)
{
char c=getchar();
t=0;
int k=1;
while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}
while(isdigit(c)){t=t*10+c-'0';c=getchar();}
t*=k;
}
int main()
{
rin(n);
for(int i=1;i<=n;i++)rin(a[i]),sum[i]=sum[i-1]+a[i];
if(sum[n]%3){puts("-1");return 0;}
ll averge=sum[n]/3;
int cnt=0;
if(sum[1]==averge)cnt=1;
for(int i=2;i<n;i++)
{
if(sum[i]==averge*2)ans+=cnt;//这里两个判断语句不能调换,想一想,为什么?
if(sum[i]==averge)cnt++;
}
cout<<ans;
return 0;
}
D.观光车
何老板带领 名游客来到一景区大门口,需要乘坐观光车游览景区。
景区提供两种观光车,一种是每辆车可以坐 名游客,包一辆车费用是 块钱;另一种每辆车可以坐b名游客,包一辆车费用是 块钱。
何老板想让这 名游客都坐上观光车,且每辆车都坐满。问何老板至少要花费多少钱?
解
设坐
辆价格为
的车,
辆价格为
的车
首先先解出不定方程
先判断是否有解,若有解:
求:
扩展欧几里得算法可以得出:
首先判断是否有解
然后:
然后便是一个一次函数在区间上求最值的问题
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b,p1,p2,n;
ll gcd(ll a,ll b,ll &x,ll &y)
{
if(b==0){x=1,y=0;return a;}
ll d=gcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-a/b*y;
return d;
}
int main()
{
cin>>n;
cin>>p1>>a>>p2>>b;
ll x,y;
ll d=gcd(a,b,x,y);
if(n%d!=0){puts("-1");return 0;}
x*=n/d;
y*=n/d;
ll m1=ceil((double)-x*d/b),m2=floor((double)y/a*d);
if(m2<m1){puts("-1");return 0;}
ll ans=p1*x+p2*y;
ll k=(p1*b-p2*a)/d;
if(k<0)ans+=k*m2;
if(k>0)ans+=k*m1;
cout<<ans;
return 0;
}
注:注意斜率以及自变量的范围
E 越狱
监狱有连续编号为
的
个房间,每个房间关押一个犯人,有
种宗教,每个犯人可能信仰其中一种。如果
相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱
输入两个整数
解
这道题从正面不是很好考虑。
所以我们从反面来思考
对于每一个房间都有
种信仰可以选择
所以总的方案数为:
在这总的方案数里,有多少是不发生冲突的呢?
对于第一个房间,他有
种信仰可以选择
对于之后的每个房间,他一定不能选与之前的房间相同的信仰,即只有
种选择
所以不发生冲突的方案有:
所以最后的答案是
#include<bits/stdc++.h>
using namespace std;
const int p=100003;
typedef long long ll;
ll mog(ll a,ll b)
{
a%=p;
ll ans=1;
while(b)
{
if(b&1){ans=(ans*a)%p;}
b>>=1;
a=(a*a)%p;
}
return ans;
}
int main()
{
long long m,n;
cin>>m>>n;
cout<<((mog(m,n)-(m%p)*mog(m-1,n-1))%p+p)%p;
return 0;
}
注:注意数据大小,避免溢出
E.看电影
何老板获得了一张电影院的免费观影卡,可以免费连续看 分钟的电影。该影院有N部电影可供选择,每部电影会在当天的不同时段放映。
何老板可以在一部电影播放过程中的任何时间进入或退出放映厅。但他不愿意重复看到一部电影,所以每部电影他最多看一次。他也不能在看一部电影的过程中,换到另一个正在播放相同电影的放映厅。
请帮何老板计算他能否做到从
到
分钟连续不断地观看电影,如果能,请计算他最少看几部电影就行了(一部电影只要看了,即使没有看完也算看了该电影)。
我们立即注意到:这道题的
取值非常小,再加同一部的电影只能看一次,所以我们很自然地就想到了状态压缩+
设
表示把s集合中的电影看完所达到的最长时间
不难得出方程:
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&-x)
typedef long long ll;
ll t[25];
ll ss[25][1005];
ll cnt[25];
ll n,l;
int logg[1<<20];
ll f[1<<20];
int main()
{
//freopen("data.in","r",stdin);
scanf("%lld%lld",&n,&l);
for(int i=1;i<=n;i++)
{
logg[1<<i]=i;
scanf("%lld%lld",&t[i],&cnt[i]);
for(int j=1;j<=cnt[i];j++)scanf("%lld",&ss[i][j]);
}
int tot=(1<<n)-1;
int ans=99;
for(int s=0;s<=tot;s++)
{
int cnt1=0;
for(int s1=s,k=lowbit(s1),s0=s^k,i=logg[k]+1;s1;s1^=k,k=lowbit(s1),s0=s^k,i=logg[k]+1)
{
cnt1++;
int tmp=upper_bound(ss[i]+1,ss[i]+1+cnt[i],f[s0])-ss[i]-1;
f[s]=max(f[s],t[i]+ss[i][tmp]);
}
if(f[s]>=l)ans=min(ans,cnt1);
}
if(ans==99)cout<<-1;
else cout<<ans;
return 0;
}
注:这个代码不知为啥只能过大部分的分,其他的分我在洛谷上下数据发现答案是一样的,但是评测显示是WA的。。。。。。