2018-2019 ACM-ICPC, Asia Shenyang Regional Contest
训练总结
本周选用了这一套题目来进行练习,感觉本套题目还是有可取之处,但是我们做的情况只能说是中规中矩,再简单题目上的速度和正确率相对之前还是有了不小的提高,多的不说了,再接再厉吧。
C Insertion Sort
题目和分析
这道题就是给定了你两个数n,k,后面的p是一个取余的数,和题意没有关系。n是数组的长度,题目里面哪个函数的意思就是可以自动将前k个数排成递增序列,问你在这n个数的全排列里面,有几个全排列经过这个函数操作后能够变成递增子序列的长度大于等于
n
−
1
n-1
n−1。
然后我们开始分析,可以知道如果
k
=
n
−
1
k=n-1
k=n−1或者
k
=
n
k=n
k=n的话那左右的全排列都满足条件,那么答案自然就是
A
n
n
A^n_n
Ann,即
n
!
n!
n!。如果是小于n-1的情况那就可以分成三部分来求解,前两部分分别是
(
n
−
2
)
×
A
k
k
(n-2)\times A_k^k
(n−2)×Akk和
(
n
−
k
)
×
A
k
k
(n-k)\times A_k^k
(n−k)×Akk,最后一部分我们是这样能够定义的:确定最后一位就是n,那么前n-1个数就是在相同k的条件下n变为n-1的答案,所以递归来写,直接加上就行了。
代码
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <stack>
#include <queue>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
ll cal(ll n,ll m,ll p)//这个函数用来计算组合数Amn
{
ll res=1;
for(ll i=0;i<m;i++)
{
res=res*(n-i)%p;
}
return res;
}
ll jisuan(ll n,ll k,ll p)//这个函数则是计算最终答案
{
if(k>=n-1)
{
return cal(n,n,p);
}
else
{
ll ans=((2*n-k-2)*cal(k,k,p))%p;//这一部分是推出来的两个结论
ans=(ans+jisuan(n-1,k,p))%p;
return ans;
}
}
void solve(int index)
{
ll n,k,p;
scanf("%lld%lld%lld",&n,&k,&p);
printf("Case #%d: ",index);
printf("%lld\n",jisuan(n,k,p));
return ;
}
int main()
{
int t = 1;
scanf("%d",&t);
for(int index=1;index<=t;index++)
{
solve(index);
}
return 0;
}
J How Much Memory Your Code Is Using?
题目和分析
这道题题意应该很好理解吧,就是让你来求所定义的变量占了多少KB,因为计算的时候是按照B来计算的,因此最后转化以下就行了,而且里面也不存在那种加逗号命名多个变量的方法,因此应该是很好判断的,我们直接写就行了。
代码
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
ll judge(char a,char b)
{
if(a=='b')
return 1;
else if(a=='c')
return 1;
else if(a=='i')
return 4;
else if(a=='_')
return 16;
else if(a=='f')
return 4;
else if(a=='d')
return 8;
else if(a=='l')
{
if(b=='l')
return 8;
else
return 16;
}
return 0;
}
void solve(int k)
{
int n;
scanf("%d",&n);
string str;
ll ans=0;
getline(cin,str);
while(n--)
{
ll temp=0;
getline(cin,str);
int len=str.size();
int left=0,right=0;
for(int i=0;i<len;i++)
{
if(str[i]=='[')
left=i;
if(str[i]==']')
right=i;
}
ll scale=1;
if(left==right)
temp=1;
for(int i=right-1;i>left;i--)
{
temp+=((str[i]-'0')*scale);
scale*=10;
}
ans+=(temp*judge(str[0],str[5]));
}
ll shang=ans/1024;
ll yu=ans%1024;
if(yu!=0)
shang++;
printf("Case #%d: %lld\n",k,shang);
}
int main()
{
int t = 1;
scanf("%d",&t);
for(int k=1;k<=t;k++)
{
solve(k);
}
return 0;
}
K Let the Flames Begin
题目和分析
虽然题目很好理解,但是要真正地做起来也不是那么容易的,主要是我们之前还是没有接触过约瑟夫环,相当于是有一个短板,算是这一次训练暴露出来了吧。至于这道题目就是用到了这个方面的相关知识,基本上不会这个知识点的话想做出这道题很难。而且这道题还在约瑟夫环的基础上进行了一些改变,
附上链接
关键公式就是这样的一个递推式:
A
(
n
,
m
)
=
(
A
(
n
−
1
,
m
−
1
)
+
k
)
%
n
A(n,m)=(A(n-1,m-1)+k)\%n
A(n,m)=(A(n−1,m−1)+k)%n
可以自己举几个例子推一下,相对比较容易就可以推出来。
在后面的计算中需要注意的:
当m比较大的时候就要进行相应的处理,而当m较小的时候直接推就行了。
处理过程如下:
因为m的范围是1e18,k的范围是1e6,而每一次都是加k的操作,因此有很多时候这个
%
n
\%n
%n很多时候都是没有用的,因此我们可以一次就将这些
k
k
k都加上,以此来降低复杂度。(这边也是看到了别人的思路,我还是太菜了)
代码
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
ll F(ll n,ll m,ll k)
{
if(m==1)return (k-1)%n;
return (F(n-1,m-1,k)+k)%n;
}
void solve(int index)
{
ll n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
printf("Case #%d: ",index);
if(k==1)
{
printf("%lld\n",m);
return ;
}
if(m<=1e6)
printf("%lld\n",F(n,m,k)+1);
else
{
ll ct=n-m+1;
ll val=(k-1)%ct;
ll now=1;
while(now<m)
{
if(val+k>=ct+1)
{
ct++;
val=(val+k)%ct;
now++;
continue;
}
ll x=(ct-val-1)/(k-1);
x=min(x,m-now);
now+=x;
val+=k*x;
ct+=x;
}
printf("%lld\n",val+1);
}
}
int main()
{
int t = 1;
scanf("%d",&t);
for(int k=1; k<=t; k++)
{
solve(k);
}
return 0;
}