T1.turnover(营业额统计)

题意:自行百度,省选题
分析:这种只需要找前驱后继的数据结构题,实在犯不着手写 Splay,SegmentTree 之类的玩意,一个 set 搞定,开了 O2 跑得飞起,不开依旧稳稳的。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <set>

using namespace std;

set<int>st;
set<int>::iterator pre,suf;
int a,n;
long long ans,pls;
int main()
{
    freopen("turnover.out","w",stdout);
    freopen("turnover.in","r",stdin);
    scanf("%d%d",&n,&a);
    st.insert(a);
    ans=a;
    for(int w=2;w<=n;w++)
    {
        scanf("%d",&a);
        pre=suf=st.lower_bound(a);
        pre--;
        pls=99999999;
        if(suf==st.begin())pls=min(pls,(long long)(*suf-a));
        else if(suf==st.end())pls=min(pls,(long long)(a-*(pre)));
        else pls=min(pls,(long long)min(a-*pre,*suf-a));
        ans+=pls;
        st.insert(a);
    }
    cout<<ans<<endl;
    return 0;
}

T2.Promotion(商品推销)

题意:不停给你一些数,找到当前最大和最小的数,“答案” 加上他们的差,然后删除他们。最后输出 “答案 “即可。
分析:这种只需要找最大最小的数据结构题,实在犯不着手写 Splay,SegmentTree 之类的玩意,一个 map 搞定,开了 O2 跑得飞起,不开好像很危险,但不会超时。

#include <iostream>
#include <cstdio>
#include <map>

using namespace std;

map<int,int>mp;
map<int,int>::iterator il,ir;
long long ans;

int main()
{
    freopen("pro.in","r",stdin);
    freopen("pro.out","w",stdout);
    int n,a,k;
    scanf("%d",&n);
    for(register int i=1;i<=n;++i)
    {
        scanf("%d",&k);
        for(register int j=1;j<=k;++j)
        {
            scanf("%d",&a);
            mp[a]++;
        }
        if(mp.empty())continue;
        il=mp.begin();
        ir=mp.end();
        ir--;
        ans+=ir->first-il->first;
        if(il->second>=2)il->second--;
        else mp.erase(il);
        if(mp.empty())continue;
        if(ir->second>=2)ir->second--;
        else mp.erase(ir);
    }
    cout<<ans<<endl;
    return 0;
}

T3.money(彩票问题)

题意:给定 1-M 的数字,求其中选出不同的 N 个使他们的倒数和为 (X/Y) 的方案有几种。(M<=50;N<=10;X,Y<=100)
分析:很显然的搜索,但是需要强效的剪枝:
首先,我们从倒数大的数往小的数搜索。
设:当前正在决策 x 是否选择,已经选择了 k 个数字,倒数和为 s,sum[i] 表示第 i 到 m 的倒数和。
1.$ X/Y-s>(sum[x]+sum[x+(n-k)])$时剪枝,意思是如果以后全选最大数都不能达到 X/Y 时剪枝
2.$ X/Y-s<(sum[m-(n-k)+1]) $时剪枝,意思是如果以后全选最小数都会比 X/Y 大时剪枝
有了这两个剪枝,再使用 long double,并将 eps 设为 1e-10~1e-12 就可以 AC 了 (1e-9 会 WA)。

#include <iostream>
#include <cstring>
#include <cstdio>

#define abs(x) (((x)<0)?(-(x)):(x))
#define MX 100
#define eps 0.0000000001

using namespace std;

typedef long double data;

data sum[MX],x,y;
int n,m,ans;

void init()
{
    sum[m+1]=0;
    for(int i=m;i>=1;i--)
        sum[i]=sum[i+1]+1.000000000/(data)i;
}

int stp[MX];

void sch(data rm,int pos,int num)
{
    if(rm<-eps)return;
    if((n-num+pos-1)>m)return;
    if(abs(rm-0)<eps&&num==n)
    {
        ans++;
        return;
    }
    if(num>=n)return;
    if(rm-sum[pos]+sum[pos+(n-num)]>eps)return;
    if(rm-sum[m-(n-num)+1]<-eps)return;
    sch(rm-1.0000000000/(data)pos,pos+1,num+1);
    sch(rm,pos+1,num);
}

int main()
{
    freopen("money.in","r",stdin);
    freopen("money.out","w",stdout);
    cin>>n>>m>>x>>y;
    init();
    sch(x/y,1,0);
    cout<<ans<<endl;
    return 0;
}

T4.game(数字游戏)

题意:给你几个数字 (2~20),两个人轮流选。每选择一个数 x,会出现以下效果:x 和之前任何一个被选的数 y 组成的 ax+by 不可被选 (0<=x,y)
求当前局面先手选哪个才可以 100% 使对方最后无数可选。
分析:注意到状态数十分的少,(2^19),所以使用状态压缩 Dp.
我们需要先预处理出每个状态中 1 的个数 (lowbit),然后预处理每种状态去掉一个1后转移到哪种状态 (去掉一个1后有些数不能选,故相当于去掉很多数)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

#define MX 524290

#define WIN 1
#define LOSE 0
#define MSTATE ((1<<19)-1)

using namespace std;

int beg;
int st[MX];
int num1[MX];
int stage[MX];
char chose[MX][30];
int mask[11];
int nexts[MX];
int pre[MX][30];

int lowbit(int x){return x&-x;}
bool cmp(int a,int b){return num1[a]<num1[b];}

int refresh(int now)
{
    int nxt=now;
    for(int i=2;i<=10;i++)
        if((now&(1<<(i-2)))==0)now&=mask[i];
    for(int i=0;i<19;i++)
    {
        for(int j=0;j+i<19;j++)
        {
            if((!(now&(1<<i)))&&(!(now&(1<<j))))
                nxt=(nxt&(MSTATE^(1<<(i+j+2))));
        }
    }
    return nxt;
}

void init()
{
    int t;
    for(int i=2;i<=10;i++)
    {
        mask[i]=MSTATE;
        for(int j=1;j*i<=20;j++)
            mask[i]=mask[i]&(MSTATE^(1<<(j*i-2)));
    }
    for(int i=1;i<=MSTATE;i++)
    {
        t=i;
        while(t)t-=lowbit(t),num1[i]++;
    }
    for(int i=1;i<=MSTATE;i++)stage[i]=i;
    sort(stage+1,stage+MSTATE+1,cmp);
    for(int i=1;i<=MSTATE;i++)nexts[i]=refresh(i);

}

void work()
{
    int now,nxt;
    for(int i=1;i<=MSTATE;i++)
    {
        if(num1[stage[i]]==1){st[stage[i]]=WIN;continue;}
        for(int j=0;j<19;j++)
        {
            now=stage[i];
            if((now&(1<<j)))
            {
                nxt=now^(1<<j);
                nxt=nexts[nxt];
                if(st[nxt]==LOSE)
                {
                    st[now]=WIN;
                    pre[now][++pre[now][0]]=j+2;
                }
            }
        }
    }
}

int main()
{
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    init();
    int a;
    while(cin>>a)beg|=(1<<(a-2));
    work();
    if(st[beg]==1)
    {
        for(int i=1;i<=pre[beg][0];i++)cout<<pre[beg][i]<<" ";cout<<endl;
    }
    else cout<<0<<endl;

    return 0;
}

T5.windoors(windoors 安装密码)

题意:给定一些字符串,忽略掉其中长度不为 25,出现了非字母的字符串。然后求其中出现最多的是哪一个。如果有多个一样多输出 0。
分析:把字符串都 hash 一下就太棒了。

#include <iostream>
#include <cstring>
#include <cstdio>

#define MX 50002
#define MD 142857

using namespace std;

typedef unsigned long long ull;

int n;
int fst[MD],nxt[MX],cnt[MX],pos[MX],hnum;
ull val[MX];

inline ull rehash(char *str)
{
    ull has=0,hh=1313131313;
    for(int i=1;i<=25;i++)has=has*hh+(*str++)-'a';
    return has;
}

void insrt(char *str,int p)
{
    ull has=rehash(str);
    for(int i=fst[has%MD];i;i=nxt[i])
        if(val[i]==has)
        {
            cnt[i]++;
            return;
        }
    nxt[++hnum]=fst[has%MD];
    fst[has%MD]=hnum;
    cnt[hnum]=1;
    val[hnum]=has;
    pos[hnum]=p;
}

char in[10005][26];

int main()
{
    freopen("windoors.in","r",stdin);
    freopen("windoors.out","w",stdout);
    int f;
    scanf("%d",&n);
    for(int w=1;w<=n;w++)
    {
        cin>>in[w];
        if(strlen(in[w])!=25)continue;
        f=0;
        for(int i=0;i<25;i++)
        {
            if(in[w][i]>='a'&&in[w][i]<='z')continue;
            else if(in[w][i]>='A'&&in[w][i]<='Z')in[w][i]=in[w][i]-('A'-'a');
            else f=1;
        }
        if(f==1)continue;
        insrt(in[w],w);
    }

    int mx=-1,p=0;
    for(int i=1;i<=hnum;i++)
    {
        if(cnt[i]>mx)
        {
            mx=cnt[i];
            p=i;
        }
    }
    for(int i=1;i<=hnum;i++)
        if(cnt[i]==mx&&i!=p)
        {
            cout<<"no solution"<<endl;
            return 0;
        }
    if(mx==-1)
    {
        cout<<"no solution"<<endl;
        return 0;
    }
    cout<<in[pos[p]]<<endl;
    return 0;
}
分类: 文章

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注