队内题目集地址 队内训练
题目集地址 The 43rd ACM International Collegiate Programming Contest Asia Shenyang Regional Contest
C Insertion Sort 思维+排列
题目地址C Insertion Sort
题目大意:构造长度为n的序列,从1到n,将序列的前k个值从小到大排序,排序后最长递增子序列的长度不小于n-1,问能构造出多少个这样的序列。
思路:因为始终有一个数的位置是可一随便摆放的,所以我按照最后一位数分别为1-n来分类
当最后一位数小于n-1时,我们构造出的序列排完序后只有一种情况出现,这样的话只需要将排完序后满足条件的序列的前k个值随机排序所构造出的序列就是满足条件的。
(
n
−
2
)
∗
A
k
k
(n-2)*A_k^k
(n−2)∗Akk
当最后一位数等于n-1时,根据n的位置推导,基本的方法跟前面是一样的,
(
n
−
2
)
∗
A
k
k
(n-2)*A_k^k
(n−2)∗Akk
然后最后一位数等于n时,情况数相当于是n等于n-1,k的情况,这个部分我们用递推的方法来做的。其实这个递推的过程可以写到一起写成一个公式。
然后还有一种找规律的方法可以参考2018 ICPC 沈阳 C Insertion Sort (数学推导规律)这个博客。
常见的数学规律 要不开跟 平方 做差 求和 要不就是 位置关系 作差 作和 差分
AC代码:
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
ll cal(ll n,ll m,ll p)
{
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));
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
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? 简单模拟
题目地址J How Much Memory Your Code Is Using?
题目大意:给你一些数据类型所占的空间,然后给你一段c++代码,让你计算这段代码中变量所占的空间。
思路:直接计算就好了。
AC代码:
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
map<string,int> msi;
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]));
}
//printf("%lld ",ans);
ll shang=ans/1024;
ll yu=ans%1024;
if(yu!=0)
shang++;
printf("Case #%d: %lld\n",k,shang);
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
// int t = 1;
int t;
scanf("%d",&t);
for(int k=1;k<=t;k++)
{
solve(k);
}
return 0;
}
K - Let the Flames Begin 约瑟夫环
题目地址K - Let the Flames Begin
题目大意:有n个人编号1-n,循环报数,报到k的人离开,接下来的人继续从1开始报数,某一个人希望自己第m个离开,问他编号应为多少。
思路:一个关键的前置知识是约瑟夫环,可以参考这篇博客【算法与数据结构】——约瑟夫环
约瑟夫环的关键就是要关注我们的目标人物的下标位置变化,假设A(n,m,q)表示n个人,数到q的人离开,第m个离开的人的位置,那么n个人的时候第m个离开的人就是n-1个人的时候第m-1个离开的人,这个人在这两次中的下标变化就是A(n,m,q)=A(n-1,m-1,q)+q,但是因为可能超过n所以要取余,他的坐标变化还是以q为单位的,我们可以得到公式。
A
(
n
,
m
,
q
)
=
(
A
(
n
−
1
,
m
−
1
,
q
)
+
q
)
m
o
d
n
A(n,m,q)=(A(n-1,m-1,q)+q)\mod n
A(n,m,q)=(A(n−1,m−1,q)+q)modn
A(n-m+1,1,q)=q-1;初始值
对应的递推代码为:
ll res=q-1;
for(int i = n-m+1;i <= n;i++)
{
res=(res+q)%n;
}
return res+1;
显然直接递推,对于 m = 1 e 18 m=1e18 m=1e18和 m = 1 e 6 m=1e6 m=1e6的数据是会超时的。观察当前递推式,每次都是+q,当超过n的范围时,进行取模运算,可以发现,当q<<n时,在很多次递推操作中都是不需要取模的,而是只有+q操作,那么我们其实可以吧加法转化成乘法来加速.
ll cal(ll n,ll m,ll k)
{
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;
}
return val;
}
AC代码:
#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;
}
else
return (f(n-1,m-1,k)+k)%n;
}
ll cal(ll n,ll m,ll k)
{
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;
}
return val;
}
void solve(int index)
{
ll n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
if(k==1)
{
printf("Case #%d: ",index);
printf("%lld\n",m);
}
else if(m<=1e6)
{
printf("Case #%d: ",index);
printf("%lld\n",f(n,m,k)+1);
}
else
{
printf("Case #%d: ",index);
printf("%lld\n",cal(n,m,k)+1);
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int t;
scanf("%d",&t);
for(int index=1;index<=t;index++)
{
solve(index);
}
return 0;
}