暑期集训2期4

购物在这里插入图片描述

把已经买过的游戏给标记一下,然后从第一款游戏开始买,如果还可以买并且没有被标记过那么买

#include<bits/stdc++.h>
using namespace std;
long long n,ans,num,x;
struct node
{
    int id;
    int tot;
}a[100010];
inline int read()
{
    int f=1;
    int res=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
    return res*f;
}
map<int ,int >vis;
int main()
{
    n=read();num=read();
    for(int i=1;i<=n;i++)x=read(),vis[x]++;
    x=0;
    for(int i=1;i<=1e9;i++)if(!vis[i]&&i+x<=num){ans++;x+=i;}//看看能买几个
    else if(i+x>num)break;
     
    cout<<ans<<endl;
    x=0;
    for(int i=1;i<=1e9;i++)if(!vis[i]&&i+x<=num){cout<<i<<' ';x+=i;}买哪几个
        else if(i+x>num)break;
    return 0;
}


数学问题

在这里插入图片描述
我们先用一个long double 类型的数,存下n的二分之三次的值,然后枚举枚举q的大小,再通过两个数去算p,分两种情况,四舍和五入,算出答案,取min

#include<bits/stdc++.h>
using namespace std;
long long n,m,a1,a2;
long double nf;
double minn=1000000000.00;
double abss(double x)
{
    if(x>0)return x;
    return -x;
}
int main()
{
    cin>>n>>m;
    nf=pow(n,(long double )2.0/3);
    for(int i=1;i<=m;i++)
    {//分四舍五入两种情况讨论
        long long t1=i*nf;
        long long t2=i*nf+1;
        double t3=abss((double)((double)((((double)(t1))/((double)(i)))-nf)));
        double t4=abss((double)((double)((((double)(t2))/((double)(i)))-nf)));
        if(t3<minn)//找最小的答案
        {
            minn=t3;
            a1=t1;
            a2=i;
        }
        if(t4<minn)
        {
            minn=t4;
            a1=t2;
            a2=i;
        }
    }
    cout<<a1<<'/'<<a2;
    return 0;
 } 


输出组合数

【输出格式】
所有的组合,每一个组合占一行且其中的元素由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。

平淡无奇的爆搜,只是要注意,输出时候,要有三的场宽

#include<bits/stdc++.h>
using namespace std;
int n,k;
inline int read()
{
    int f=1;
    int res=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
    return res*f;
}
int ans[250],vis[250];
inline void pintf()
{
    for(int i=1;i<=k;i++)cout<<setw(3)<<ans[i];//场宽
    putchar('\n');
}
inline void dfs(int last,int x)
{
    if(x==k)
    {
        pintf();
        return;
    }
    for(int i=last+1;i<=n;i++)
    {
        if(!vis[i])
        {
            vis[i]=1;
            ans[x+1]=i;
            dfs(i,x+1);
            vis[i]=0;//还原现场
        }
    }
}
int main()
{
    n=read();k=read();
    dfs(0,0);
    return 0;
}


取石头

在这里插入图片描述
SG函数
一个公平游戏(impartial game)可以抽象为:在一个DAG上有一枚棋子,两人轮流移动它,不能移动者输。
SG函数的定义如下。没有出度的点的SG值为0,其它点的SG值为它的后继的SG值的mex。即SG(u)=mex{SG(v)},u→vSG(u)=mex{SG(v)},u→v。
在SG值为0的点上,先手必败,而在其它点上先手必胜。
数据规模较大时,一般来说,可以通过打表找到SG函数的通项。
其它时候可以递推求。求mex用Trie树上的跳法。

SG函数
定义mex{S}运算: 不在集合S中最小的非负整数。
例如: mex{0,2,3,4}=1,mex{1,2,3,4}=0。
对于任意状态x,SG(x)=mex{S}SG(x)=mex{S},S是x后继状态SG函数值的集合。
若x的后继三个SG函数值为SG(a),SG(b),SG©SG(a),SG(b),SG©,那么SG(x)=mex{SG(a),SG(b),SG©}SG(x)=mex{SG(a),SG(b),SG©}。
当x时终态时,后继状态为空,SG(x)=0SG(x)=0,即为必败态。
有1堆n个的石子,每次只能取{ 1, 3, 4 }个石子,先取完石子者胜利,
那么各个数的SG值为多少?
1
2
SG(0) = 0, f[ ] = {1,3,4}
x = 1时,可取1个,剩余0个,SG(1) = mex{ SG(0) } = 1。
x = 2时,可取1个,剩余1个,SG(2) = mex{ SG(1) } = 0。
x = 3时,可取1个或3个,剩余2个或0个,SG(3) = mex{ SG(2), SG(0) } = 1。
x = 4时,可取1,3,4,剩余3,1,0,SG(4) = mex{ SG(3), SG(1), SG(0) } = 2。
可以发现,SG函数可以从小到大递推而来。
由上述实例我们就可以得到SG函数值求解步骤,那么计算1~n的SG函数值步骤如下:
使用 数组f 将 可改变当前状态 的方式记录下来。
然后我们使用 另一个数组 将当前状态x 的后继状态标记。
最后模拟mex运算,也就是我们在标记值中 搜索 未被标记值 的最小值,将其赋值给SG(x)。
我们不断的重复 2 - 3 的步骤,就完成了 计算1~n 的函数值。
一个游戏可以分解成两个子游戏a,b,那么即后继的状态为SG[a]^SG[b]的值

#include<bits/stdc++.h>
using namespace std;
long long n,m,x;
long long ans;
int vis[2];
inline int mex(int x,int y)
{
 
    vis[0]=vis[1]=vis[2]=0;
    vis[x]=1;vis[y]=1;
    for(int i=0;i<=2;i++)
    if(!vis[i])return i;
}
inline int read()
{
    int res=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
    return res;
}
inline int sg(int x)
{
    if(x==0)return 0;
    if(x%2)
    {
        if(x<=3)return mex(sg(x-1),2);
        else return 0;
    }
    else return mex(sg(x-1),sg(x/2)); 
}
int main()
{
    int t;t=read();
    while(t--)
    {
      ans=0;
      n=read();m=read();
      for(int i=1;i<=n;i++)
      {
        x=read();
        if(!(m%2)&&x>2)ans^= !(x%2);//偶数情况 如果 k>1也就是 x>2情况下   偶数堆 0 奇数堆 1   
        else if(!(m%2)&&x<=2)ans^=x;//偶数情况 如果 k<=1也就是x<=2情况是  输出堆数 
        else ans^=sg(x);//奇数情况 
      }
 
      if(ans)cout<<"YJC"<<endl;
      else cout<<"CJY"<<endl;
    }
 
    return 0;
}

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值