topcoder 乱作系列1 srm500 srm501 srm502

博客详细介绍了作者在TopCoder SRM 500, 501, 502三场比赛中遇到的题目,并对各个分值段的问题进行了分析和解答思路的分享。涉及动态规划、模拟、贪心等算法,包括概率计算、坐标处理、状态转移等技巧。" 122931606,7800548,Python绘制人口金字塔图:数据可视化详解,"['Python', '数据分析', '数据可视化']
部署运行你感兴趣的模型镜像

SRM 500 250pts

答案一定是被选次数最多的人被选的概率,模拟第一次后的结果,考虑剩下的个数。以后每次剩下的都是用n%当前的个数。

#include <bits/stdc++.h>
using namespace std;
#define N 510
int val[N];
class MafiaGame
{
    public:
    double probabilityToLose(int n,vector<int>a)
    {
        int mx=0;double ret=1;
        for(int i=0;i<a.size();i++)
            val[a[i]]++,mx=max(mx,val[a[i]]);
        int tot=n-a.size(),cnt=0;
        for(int i=0;i<n;i++)
            if(val[i]<=mx-1)
                tot-=mx-1-val[i],val[i]=mx-1;
            else cnt++;
        if(tot>0)cnt+=tot;          
        if(mx==0)cnt=n;
        while(cnt!=1)
        {
            if(n%cnt==0)return 0;
            ret=ret*(n%cnt)/cnt;
            cnt=n%cnt;
        }
        return ret;
    }
}tmp;

SRM 500 500pts

搜搜搜!!!! 扒了题解的代码,感觉还是很妙的,避免精度误差。
由于给出的坐标都是整数,所以边界上递归下去的并不多。

#include <bits/stdc++.h>
using namespace std;
#define N 510
int val[N];
class FractalPicture
{
    double cal(int deep,int x1,int x2,int y1,int y2)
    {
        x1=max(x1,-27);x2=min(x2,27);
        y1=max(y1,0);y2=min(y2,81);
        if(x1>x2||y1>y2)return 0;
        if(x1==-27&&x2==27&&y1==0&&y2==81)
            return deep*54+27;
        if(deep==1)
        {
            if(x1>0||x2<0)return 0;
            return y2-y1;
        }
        double ret=0;
        if(x1<=0&&x2>=0&&y1<=54)
            ret=min(y2,54)-y1;
        return ret+1.0/3*(
            cal(deep-1,x1*3,x2*3,(y1-54)*3,(y2-54)*3)+
            cal(deep-1,(y1-54)*3,(y2-54)*3,x1*3,x2*3)+
            cal(deep-1,(y1-54)*3,(y2-54)*3,-x2*3,-x1*3));
    }
    public:
    double getLength(int x1,int y1,int x2,int y2)
    {return cal(500,x1,x2,y1,y2);}
}tmp;

SRM 500 1000pts

1,2,4,8,6,3,9的个数不固定,手动枚举2,4,8,9的个数可以确定1,3的个数。
/*
注 意 枚 举 顺 序!!!!!!!!!!!!!!!!!!!!!!
妈蛋枚举的时候顺序不对搞出负数本机和tc测的不一样调了半天我会乱说?
(手动注释)*/
对于每一数字单独计算个数,设i出现的次数为ai,那么考虑某个ai 出现在指定的一位,那么它对答案的贡献为:
(n1)!ai!a1!a2!...a10!(ai1)!i
预处理一个ten[i]=10i+10i1+....+1 就可以计算该情况下每个数字的贡献了。

#include <bits/stdc++.h>
using namespace std;
#define mod 500500573
#define ll long long
#define N 2600
ll ten[N],jc[N],njc[N];
int cnt[21];
ll qpow(ll x,ll y)
{
    ll ret=1;
    while(y)
    {
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }
    return ret;
}
class ProductAndSum
{
    public:
    int getSum(int p2,int p3,int p5,int p7,int sum)
    {
        ll ret=0;
        ten[0]=1;jc[0]=njc[0]=1;

        for(int i=1;i<=sum;i++)jc[i]=jc[i-1]*i%mod;
        njc[sum]=qpow(jc[sum],mod-2);
        for(int i=sum-1;i>=1;i--)njc[i]=njc[i+1]*(ll)(i+1)%mod; 

        for(int i=1;i<=sum;i++)ten[i]=(ten[i-1]*10ll)%mod;
        for(int i=1;i<=sum;i++)ten[i]=(ten[i]+ten[i-1])%mod;

        for(cnt[2]=0;cnt[2]<=p2;cnt[2]++)
            for(cnt[4]=0;cnt[4]*2+cnt[2]<=p2;cnt[4]++)
                for(cnt[8]=0;cnt[8]*3+cnt[4]*2+cnt[2]<=p2;cnt[8]++)
                {
                    cnt[6]=p2-cnt[2]-cnt[8]*3-cnt[4]*2;
                    for(cnt[9]=0;cnt[6]+cnt[9]*2<=p3;cnt[9]++)
                    {
                        cnt[5]=p5;cnt[7]=p7;
                        cnt[3]=p3-cnt[6]-cnt[9]*2;
                        cnt[1]=sum;
                        for(int w=2;w<=9;w++)
                            cnt[1]-=cnt[w]*w;
                        if(cnt[1]<0)continue;
                        int tot=0;ll mul=1;
                        for(int w=1;w<=9;w++)
                            tot+=cnt[w],mul=mul*njc[cnt[w]]%mod;
                        mul=mul*jc[tot-1]%mod;
                        for(int w=1;w<=9;w++)
                            ret=(ret+ten[tot-1]*mul%mod*jc[cnt[w]]%mod*njc[cnt[w]-1]%mod*w%mod)%mod;
                    }
                }
        return ret;
    }
}tmp;

SRM 501 250pts

f[i][j] 表示走i步A,j步B的最大得分,g[i][j] 表示最小得分,dp一下就好了。

#include <bits/stdc++.h>
using namespace std;
double f[52][52],g[52][52];
class FoxPlayingGame
{
    public:
    double theMax(int A,int B,int v1,int v2)
    {
        f[0][0]=0;
        for(int i=0;i<=A;i++)
            for(int j=0;j<=B;j++)
                {
                    if(i||j)f[i][j]=-1e100,g[i][j]=1e100;
                    if(i)
                    {   
                        f[i][j]=max(f[i][j],f[i-1][j]+(double)v1/1000.0);
                        g[i][j]=min(g[i][j],g[i-1][j]+(double)v1/1000.0);
                    }
                    if(j)
                    {   
                        f[i][j]=max(f[i][j],f[i][j-1]*v2/1000.0);
                        g[i][j]=min(g[i][j],g[i][j-1]*v2/1000.0);
                        f[i][j]=max(f[i][j],g[i][j-1]*v2/1000.0);
                        g[i][j]=min(g[i][j],f[i][j-1]*v2/1000.0);
                    }
                }
        return f[A][B];
    }
}tmp;

SRM 501 500pts

f[i][S][j][k] 表示到第i个点,总和为S,第i-1个点的值为j,第i-2个点是否大于第i-1个点的方案数。

#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
int f[41][1610][41][2],n;
class FoxAverageSequence
{
    public:
    int theCount(vector<int> a)
    {
        n=a.size();
        f[0][0][0][0]=1;
        for(int i=0;i<n;i++)
            for(int j=0;j<=i*40;j++)
                for(int k=0,t;k<=40;k++)
                {
                    if((t=a[i])!=-1)
                    {
                        if(i==0||t<=j/i)
                        {   
                            if(t<k)(f[i+1][j+t][t][1]+=f[i][j][k][0])%=mod;
                            else
                            {
                                (f[i+1][j+t][t][0]+=f[i][j][k][0])%=mod;
                                (f[i+1][j+t][t][0]+=f[i][j][k][1])%=mod;
                            }
                        }
                    }
                    else for(t=0;t<=40;t++)
                    {
                        if(i!=0&&t>j/i)break;
                        if(t<k)(f[i+1][j+t][t][1]+=f[i][j][k][0])%=mod;
                        else
                        {
                            (f[i+1][j+t][t][0]+=f[i][j][k][0])%=mod;
                            (f[i+1][j+t][t][0]+=f[i][j][k][1])%=mod;
                        }
                    }
                }
        int ans=0;
        for(int i=0;i<=n*40;i++)
            for(int j=0;j<=40;j++)
                for(int k=0;k<=1;k++)
                    (ans+=f[n][i][j][k])%=mod;
        return ans;
    }
}tmp;

SRM 501 1000pts

f[i][j] 表示到第i个点,左右走了j步的最小花费,把坐标相同的放在一起,
按纵坐标排序,把横坐标相同的放在一起搞。

可以用f[i][jabs(x[i],x[i])] 更新f[i][j]
把绝对值拆开,对于x坐标小于当前x坐标的点f[i][j+x[i]]=max(f[i][j+x[i]])
对于x坐标大于当前x坐标的点f[i][jx[i]]=max(f[i][jx[i]])
这个东西可以拿树状数组维护。这样就非常妙了。

对于横坐标相同的从两边各更新一遍,最后合在一起统计一下。
//虽然说tc里dp多,为什么一场都是dp呀。。。

#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define ll long long 
#define N 1100
int w,h,n,m,v,tx,ty;
struct node
{
    int x,y,v;
    friend bool operator < (const node &r1,const node &r2)
    {
        if(r1.y==r2.y)return r1.x<r2.x;
        return r1.y<r2.y;
    };
}a[N],tmp[N];
int f[N][N],t[N][N];
struct diw_tree
{
    int tr[N];
    void insert(int x,int y)
    {
        x++;
        for(int i=x;i<=w+5;i+=i&-i)
            tr[i]=max(tr[i],y);
    }
    int query(int x)
    {
        x++;int ret=0;
        for(int i=x;i;i-=i&-i)
            ret=max(ret,tr[i]);
        return ret;
    }
}A[N<<1],B[N<<1];
class FoxSearchingRuins
{
    ll solve()
    {
        int tn=0;a[0].x=-1;
        for(int i=1;i<=n;i++)
        {
            if(a[i].x!=a[i-1].x||a[i].y!=a[i-1].y)tn++;
            tmp[tn].x=a[i].x;tmp[tn].y=a[i].y;tmp[tn].v+=a[i].v;
        }
        n=tn;for(int i=1;i<=n;i++)a[i]=tmp[i];
        sort(a+1,a+1+n);
        for(int l=1,r;l<=n;l=r+1)
        {
            for(r=l;r<=n&&a[r].y==a[l].y;r++);r--;
            for(int i=l;i<=r;i++)
            {
                for(int j=a[i].x;j-a[i].x<=m;j++)
                    f[i][j-a[i].x]=max(f[i][j-a[i].x],B[j].query(w-a[i].x)+a[i].v);
                for(int j=-a[i].x;j+a[i].x<=m;j++)
                    f[i][j+a[i].x]=max(f[i][j+a[i].x],A[j+w].query(a[i].x)+a[i].v);
            }   
            for(int i=l;i<=r;i++)
                for(int j=0;j<=w;j++)t[i][j]=f[i][j];
            for(int i=l+1;i<=r;i++)
                for(int j=a[i].x-a[i-1].x;j<=w;j++)
                    t[i][j]=max(t[i][j],t[i-1][j-(a[i].x-a[i-1].x)]+a[i].v);

            for(int i=r-1;i>=l;i--)
                for(int j=a[i+1].x-a[i].x;j<=w;j++)
                    f[i][j]=max(f[i][j],f[i+1][j-(a[i+1].x-a[i].x)]+a[i].v);

            for(int i=l;i<=r;i++)
                for(int j=0;j<=w;j++)f[i][j]=max(f[i][j],t[i][j]);

            for(int i=l;i<=r;i++)
            {   
                for(int j=a[i].x;j-a[i].x<=m;j++)
                    B[j].insert(w-a[i].x,f[i][j-a[i].x]);
                for(int j=-a[i].x;j+a[i].x<=m;j++)
                    A[j+w].insert(a[i].x,f[i][j+a[i].x]);
            }   
        }
        ll ans=1ll<<61;
        for(int i=1;i<=n;i++)
            for(int j=0;j<=m;j++)
                if(f[i][j]>=v)
                {   
                    ans=min(ans,(ll)tx*j+(ll)ty*a[i].y);
                }
        return ans==1ll<<61 ? -1:ans;
    }
    public:
    ll theMinTime(int W, int H, int jewelCount, int LR, int goalValue, int timeX, int timeY, vector <int> seeds)
    {
        w=W;h=H;n=jewelCount;
        m=LR;v=goalValue;tx=timeX;ty=timeY;
        a[1].x=((ll)seeds[1]*seeds[0]+seeds[2])%W;
        a[1].y=((ll)seeds[4]*seeds[3]+seeds[5])%H;
        a[1].v=((ll)seeds[7]*seeds[6]+seeds[8])%seeds[9];;
        for(int i=2;i<=n;i++)
        {
            a[i].x=((ll)seeds[1]*a[i-1].x+seeds[2])%W;
            a[i].y=((ll)seeds[4]*a[i-1].y+seeds[5])%H;
            a[i].v=((ll)seeds[7]*a[i-1].v+seeds[8])%seeds[9];
        }
        return solve();
    }
}cls;

SRM 502 250pts

如果有其他字符串是当前字符串的后缀就去掉当前字符串,否则加上概率就行了。

#include <bits/stdc++.h>
using namespace std;
int n;
double per[11];
int cal(string x,string y)
{
    if(x.size()<y.size())return 0;
    for(int i=y.size();i>=1;i--)
        if(x[x.size()-i]!=y[y.size()-i])return 0;
    return 1;
}
int cmp(string x,string y)
{
    if(x.size()==y.size())
        return x<y;
    return x.size()<y.size();
}
class TheLotteryBothDivs
{
    public:
    double find(vector <string> a)
    {
        per[0]=1;double ans=0;
        for(int i=1;i<=10;i++)per[i]=per[i-1]/10;
        n=a.size();
        sort(a.begin(),a.end(),cmp);
        for(int i=0;i<n;i++)
        {
            cout<<a[i]<<endl;
            int flag=0;
            for(int j=0;j<i;j++)
                if(cal(a[i],a[j]))
                    {flag=1;break;}

            if(!flag)ans+=per[a[i].size()];
        }
        return ans;
    }   
}cls;

SRM 502 500pts

贪心,按pointsPerMinuterequiredTime 从小到大排序,最后选出的做的题一定是按这个顺序排序。官网上有证明,大概是考虑交换相邻两个之后的情况。
排序之后dp,设f[i][j] 表示现在做到第i个题,剩余时间为j的最大得分。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
struct node
{
    int v,dec,req;
    friend bool operator < (const node &r1,const node &r2)
    {return (ll)r1.dec*r2.req>(ll)r2.dec*r1.req;};
}a[51];
int f[51][110000],n;
class TheProgrammingContestDivOne
{
    public:
    int find(int T, vector <int> v, vector <int> dec, vector <int> req)
    {
        n=v.size();
        for(int i=1;i<=n;i++)
        {
            a[i].v=v[i-1];
            a[i].dec=dec[i-1];
            a[i].req=req[i-1];
        }
        sort(a+1,a+1+n);
        f[0][T]=0;
        for(int i=1;i<=n;i++)
            for(int j=0;j<=T;j++)
            {
                f[i][j]=f[i-1][j];
                if(j+a[i].req<=T)
                    f[i][j]=max((ll)f[i][j],(ll)f[i-1][j+a[i].req]+a[i].v-(ll)(T-j)*a[i].dec);
            }
        int ans=0;
        for(int i=0;i<=T;i++)ans=max(ans,f[n][i]);
        return ans;
    }   
}cls;

SRM 502 1000pts

这题好神呀!!
f[d][k][a] 为 方程x1+x2+....+xk1+axk0(modd) 的解的个数。其中x1xk[0,n) 内的整数。
sum=x1+x2+....+xk1g=gcd(a,d)
那么方程有解的充要条件为sum%g=0 且在[0,d) 内的解的个数有g 个。那么在[0,n) 的解有gnd个。
所以这部分答案为f[g][k1][1]gnd
由可能出现xk 与其他x相等的情况,所以需要减去f[g][k1][a+1](k1), 因为每一个都重复出现了k1次。
因此f[d][k][a]=f[g][k1][1]gndf[g][k1][a+1](k1)
第一维只能是n的约数,第三维只能是1或K+1-k。
用map存一下状态,记忆化搜一下。

#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define ll long long
map<int,int>ma[1100][2];
int n,k1;
int gcd(int x,int y)
{return y==0 ? x:gcd(y,x%y);}
int F(int d,int k,int a)
{
    if(k==0)return 1;
    int a1=a==1 ? 0:1;
    if(ma[k][a1].count(d))return ma[k][a1][d];
    int g=gcd(a,d);
    ma[k][a1][d]=((ll)F(g,k-1,1)*g%mod*(n/d)%mod-(ll)F(d,k-1,(a+1)%d)*(k-1)%mod+mod)%mod;
    return ma[k][a1][d];
}
int qpow(int x,int y)
{
    int ret=1;
    while(y)
    {
        if(y&1)ret=(ll)ret*x%mod;
        x=(ll)x*x%mod;y>>=1;
    }
    return ret;
}
class TheCowDivOne
{
    public:
    int find(int N,int K)
    {
        n=N;k1=K+1;
        int ans=F(N,K,1);
        int jc=1;
        for(int i=1;i<=K;i++)jc=(ll)jc*i%mod;
        return (ll)ans*qpow(jc,mod-2)%mod;
    }
}cls;   

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值