先在开头吐槽一下这场比赛修改了n次题面甚至改了数据,题面的糟糕程度实属第一次见。
这场比赛难的不是题目,是出题人,这场比赛可能是没有经过严格的验题。
A题
相关tag:数学
如果我们把1划分成x份,那么每份就是1/x。
我们希望最后的k个人得到的尽可能平均,那么每个人必然是拿到[x/k]份的1/x或者[x/k]+1份的1/x。
那么每个人得到的值,与平均值x/k的差值是不可能大于1/x的。
题目要求差值不超过1/210,那么我们把1切成x=1024份,每份的值为1/1024,再平均分配打包给所有人就是了。
切割成1024份需要的次数为20+21+22+…+29=1023次,在加上打包k次,次数必定在6000次内。
(当然你切成2048份也行,仍然在6000次内)
#include
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=30+7;
const int mod=998244353;
int k;
int ans=1023;
int main()
{
scanf("%d",&k);
printf("%d\n",ans+k);
int now=1;
for(int i=0;i<=9;i++)
{
for(int j=0;j<now;j++)
printf("1 %d\n",i);
now*=2;
}
int num=1024;
int a=num/k,b=num%k;
for(int i=1;i<=k;i++)
{
printf("2");
if(i<=b)
{
printf(" %d",a+1);
for(int i=0;i<=a;i++) printf(" 10");
printf("\n");
}
else
{
printf(" %d",a);
for(int i=0;i<a;i++) printf(" 10");
printf("\n");
}
}
}
B题
相关tag:前缀和
使用num[i]记录第i个数字为多少。
使用sum[i]记录前缀和,也就是前i个数字值的和。
如果某一个区间[l,r]的数字加起来为k的整数倍。
那么必定有(num[r]-num[l-1])%k=0。
也等价于num[r]%k=num[l-1]%k
我们可以依靠前缀和对k取模的结果。
cas[i]记录前缀和对k取模为i的前缀和,第一次出现在哪里。
每次出现取模为i的前缀和时,用当前位置减去第一次出现的位置,即为该位置为右边界可以找到的最长区间了。
#include
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=30+7;
ll ans,n,k;
ll num[100007];
ll cas[100007];
int main()
{
int t;scanf("%d",&t);
while(t--)
{
ans=-1;
for(int i=1;i<100007;i++) cas[i]=-1;
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&num[i]);
num[i]=(num[i]+num[i-1])%k;
if(cas[num[i]]==-1) cas[num[i]]=i;
else ans=max(ans,i-cas[num[i]]);
}
printf("%lld\n",ans);
}
}
C题
相关tag:数学
首先从左往右看,我们可以按照连续的不下降区间,把整个数组划分成各块区间。
由于相邻两个区间之间,相邻的那两个数的值必定是下降的。
因此我们选择满足条件的子数组,只能在每块区间里找。
对于一个长度为x的区间。
长度为1的连续子数组有x种取法。
长度为2的连续子数组有x-1种取法。
…
长度为x的连续子数组用1种取法。
该区间的总取法为(1+2+…+x)=x × \times × (x+1)/2种。
所有区间的取法加起来即可。
#include
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=30+7;
ll ans=0,n;
ll num[100007];
ll cas=0;
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&num[i]);
if(num[i]>=num[i-1]) cas++;
else
{
ans+=cas*(cas+1)/2;
cas=1;
}
}
ans+=cas*(cas+1)/2;
printf("%lld\n",ans);
}
D题
相关tag:简单博弈
只有1张牌的时候,先手必输。
有x=[2,k+1]张牌的时候,先手的人可以拿走x-1张牌,剩下1张,所以先手必胜。
有x=k+2张牌的时候,先手的人不管拿走[1,k]的任意张数,剩下的数量必定落在[2,k+1]的区间里,先手必输。
当x=[k+3,2k+2]张牌的时候,同上,先手必胜
当x=2k+3张牌的时候,同上,先手必输。
…
归纳后得到,当x=d × \times ×(k+1)+1时候(d为常数),先手必输,其他情况先手必胜。
#include
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+7;
int main()
{
int t;scanf("%d",&t);
while(t--)
{
int n,k;scanf("%d%d",&n,&k);
if((n-1)%(k+1)==0) printf("ma la se mi no.1!\n");
else printf("yo xi no forever!\n");
}
}
E题
相关tag:博弈
(出题人题面的输出和实际样例的输出都能不一样的,一个是no.1!,一个是no1.!。一个题就算了,好几个题的题面都有问题,是真的绝活)
首先我们算出在乌龟上方和下方分别有a和b张牌。
如果a>b的话,我们交换一下a,b的值,保证a<=b。
接下来,按照a的值为0,1,2,…15e5分类讨论。
a=0的情况:
a=0,b=0的时候,先手负。
a=0,b>0的时候,先手可以一次拿光b,先手胜。
a=1的情况:
a=1,b=1的时候,先手a和b都拿掉1,先手胜。
a=1,b=2的时候,先手如果想赢,那么就必须要让当前的状态变为先手负(操作之后当前后手的人变为下次操作的先手),而之前的先手负的状态只有a=0,b=0,我们从a=1,b=2的状态怎么取都是得不到a=0,b=0的。因此此时先手负。
a=1,b>2的时候,我们可以把b拿掉b-2的部分,使得变成a=1,b=2的先手负(当前的后手)状态。因此先手胜。
a=2的情况:
a=2,b>=2的所有状况,都可以从b取走b-1个,使得变为a=2,b=1也就是a=1,b=2的情况。先手必胜。
a=3的情况:
a=3,b=3或等于4的时候,可以同时从a和b中拿掉3或者2,得到a=0,b=0或者a=1,b=2。因此先手胜。
a=3,b=5的时候,前面的先手负状态只有a=0,b=0或者a=1,b=2,我们不论如何操作都无法得到这两种状态。因此先手负。
a=3,b>5的时候,先手可以从b拿走b-5个,使得剩下a=3,b=5个。因此先手胜
…
归纳后实际上会发现,当a>0的情况下, 最开始的时候先手负状态只有a=0,b=0,a和b差值为0, 注意到这里跳过了a=2的状态。这是因为在a<2的状态中有一个a=1,b=2的状态是先手负。 也就是说,对于我们当前a=x的状态,如果在前面的a 由此综上,用一个dis记录下一个先手负状态a和b的差值应该为多少。 那么们可以一路从小到大去循环a的值, 以上。 F题 数据很大,一开始交了一发试一下,果然tle了。 G题 看注释吧 H题 m只有0,1,2三种状态,稿纸上推演一下,总结规律即可。 I题 如果我们选择了k天,第一天选择了d朵。 J题 模拟,没什么好说的。大一同学去学一下如何存图。 K题 注意一下字符为z或者Z的特殊情况即可。 L题 很好的一个二分答案例题。 对于每对相邻的车站,他们的距离如果为dis,我们check的距离为x。
对于某一类a=x,先手如果想赢,当b还不是很大的状态下,只能通过同时取走a和b一部分值,得到a
后续有a=1,b=2,a和b差值为1。
再后续a=3,b=5,a和b差值为2。
再后续a=4,b=7,a和b差值为3。
…
我们可以把b取b-2个,变为上述状态来获胜。
使用cas[i]记录对于a=i,b=cas[i]时先手负,cas[i]=-1时代表此时b不论取什么值先手必胜。
如果当前的a值,没有在前面计算的b中出现过,代表当前a的值存在一种先手负的状态,当b=a+dis时必败,并且更新cas[b]=-1。
如果当前的a值已经在前面计算的b中出现过,也就是说cas[a]=-1,那么当前a的值必胜。#include
相关tag:模拟,哈希,卡时间(那你为什么要交)
加了个快读,用了unordered_map这个O(1)的哈希map就过掉了。#include
相关tag:贪心#include
相关tag:简单规律,快速幂#include
相关tag:数学,暴力
那么总的花的数量就是d × \times × (20+21+…+2k-1)
也就是说对于选择第k天的情况来说,如果存在满足的整数d,那么总的花数量n需要满足n能整除 (20+21+…+2k-1)
因此我们直接处理出k=2到15这些天数的 (20+21+…+2k-1),用n一一去暴力尝试整除即可。#include
相关tag:模拟#include
相关tag:模拟
另外出题人爬#include
相关tag:二分答案
对于我们最后站台之间相邻距离的最大值L,随着L的增大,我们需要设置的站台数量只可能变少不可能变多。满足二分条件。存在某一个值x,使得当L>=x时,站台数量<=k。
可以借此写出一个二分。
那么这段距离之中除了左右两侧已经有的城市外,需要的站台数量就是(dis/x+dis%x?1:0)-1。
由此算出距离x对应需要的最少站台数量sum,用sum与题目要求k对比即可check正确性。#include