The Preliminary Contest for ICPC Asia Shanghai 2019 赛后补题

The Preliminary Contest for ICPC Asia Shanghai 2019 赛后补题

比赛链接:传送门

这次比赛过程中大部分都是队友A的,所以这里补一下自己没有A到的题和没A的题。

这次又出到了Bell 数的相关知识,跟昨天的网络赛有相似的题但是还没时间补。尽快把不会的知识点补全。

增加了退背包的思想。

G题hash字符串还未补

B:Light bulbs

定位:简单题

题意:t组输入,有一排长度为n的灯泡,初始状态都是关闭,接下来m次操作,每次操作使得区间 [ l , r ] [l,r] [l,r] 灯泡状态反转,问最后有多少个灯泡开着的。 t ∈ [ 1 , 1000 ] , n ∈ ( 1 , 1 e 6 ) , m ∈ [ 1 , 1000 ] t\in[1,1000],n\in(1,1e6),m\in[1,1000] t[1,1000],n(1,1e6),m[1,1000]

思路:这是明显的区间异或,我们可以将区间异或变成异或差分的形式。这样每组时间复杂度为 O ( n ) O(n) O(n),但是t组却超时了,我们注意到m很小,所以我们处理出所有变化的点,然后排序统计答案即可。每组的时间复杂度为 O ( m l o g m ) O(mlogm) O(mlogm)

代码

#include <bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef pair<int,int> P;
const int N=1e6+20;
int sq[N];
int main()
{
    int n,m,t,cas=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        int top=0;
        for(int i=1;i<=m;++i){
            int l,r;
            scanf("%d%d",&l,&r);
            l++,r++;
            sq[top++]=l;
            sq[top++]=r+1;
        }
        sort(sq,sq+top);
        int ans=0,pe=0,last=1;
        for(int i=0;i<top;++i){
            if(pe&1)    ans+=sq[i]-last;
            last=sq[i];
            pe^=1;
        }
        printf("Case #%d: %d\n",++cas,ans);
    }
}

D:Counting Sequences I

定位:中等偏难

题意:给出一个n,求满足条件 a 1 + a 2 + a 3 . . . + a n = a 1 ∗ a 2 ∗ a 3 . . . ∗ a n a_1+a_2+a_3...+a_n=a_1*a_2*a_3...*a_n a1+a2+a3...+an=a1a2a3...an的序列的个数。

思路:我们可以看出来序列的顺序改变不影响等式成立,并且模拟几下可以看出来总有 n − 2 n-2 n2个1,一个2,一个n的序列满足条件,并且当次大的数增大1时,最大的数减小速度快,所以我们可以推断出这个序列的和不超过 2 ∗ n 2*n 2n,并且不考虑位置的情况下,满足条件的序列数目应该很少,所以我们可以暴力搜索+大剪枝。然后对于每一个序列计算该序列考虑位置的组合数即可

只要知道前面的信息,那么随意剪枝就行。

这题因为check快速幂的一个小错误,检测a^b是否大于k,应该先让b/2,再判断a是否在于k。找了1h30min的bug…

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const ll N=3000+20;
const ll mod=1e9+7;
ll qpow(ll a,ll b,ll m)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%m;
        a=a*a%m;
        b>>=1;
    }
    return ans;
}
int ok;
ll ckpow(ll a,ll b,ll limt)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
        {
            ans=ans*a;
            if(ans>limt)
                return -1;
        }
        b>>=1;
        a=a*a;
        if(b!=0&&a>limt)
            return -1;

    }
    return ans;
}
ll inv(ll a)
{
    return qpow(a,mod-2,mod);
}
ll f[N],g[N];//i!,i!逆元
void init()
{
    f[0]=1;
    for(ll i=1; i<=3000; ++i) f[i]=(f[i-1]*i)%mod;
    for(ll i=1; i<=3000; ++i)
    {
        g[i]=inv(f[i]);
    }
}
ll calc(ll w[],ll n)
{
    ll ans=f[n];
    map<ll,ll> mmp;
    for(ll i=1; i<=n; ++i) mmp[w[i]]++;
    for(P p:mmp)
    {
        ans=ans*g[p.second]%mod;;
    }
    return ans;
}
ll s[3005],res,n;
void dfs(ll ps,ll pt,ll limt,ll k)
{

    if(pt > 2*n) return ;
    if(pt > ps &&limt > 1) return ;
    if((n-k+1)*limt + ps> 2*n) return ;//数列和优化
    ll si=ckpow(limt,n-k+1,2*n);
    if(limt>1 &&si==-1) return ;//数列乘积优化
    if(limt>1&& si*pt>2*n) return ;//数列乘积优化

    if(k > n){
        if(ps==pt){
            res=(res+calc(s,n))%mod;
        }

        return ;
    }
    for(ll i=limt;i<=n;++i){
        int si=ckpow(i,n-k,2*n);
        if(si==-1||si*i*pt>2*n) return ;
        s[k]=i;
        dfs(ps+i,pt*i,i,k+1);
    }
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    init();
    ll t;
    cin>>t;
    while(t--)
    {
        cin>>n;
        res=0;
        dfs(0,1,1,1);
        cout<<res<<endl;
    }
    return 0;
}

J:Stone game

定位:中等偏难

题意:给出n个石头,每个石头的价值为 a i a_i ai,求有多少种取法使得,取的石头价值和大于等于未取的石头价值和,且满足从拿到的石头中任意减去一个石头的价值使得 剩下的价值小于等于未取的石头价值和。 n ∈ [ 1 , 300 ] , a i ∈ [ 1 , 500 ] n\in[1,300],a_i\in[1,500] n[1,300],ai[1,500]

思路:01退背包思想,然后枚举最小值计算满足其题意的解的个数即可。

我们首先将物品从小到大排序,先计算出01背包满足总价值为 j j j 的取法个数。然后退去物品 a 1 a_1 a1求出满足总价值为 j j j 的取法个数,满足条件的价值是一个区间 [ ( s u m + 1 ) / 2 − a i , ( s u m − a i ) / 2 ] [(sum+1)/2-a_i,(sum-a_i)/2] [(sum+1)/2ai,(sumai)/2],统计其种类数即可。接下来就是枚举 a 2 a_2 a2是所取集合中最小的物品,此时我们只需要将 a 1 , a 2 ​ a_1,a_2​ a1,a2从背包中退去即可,然后模仿上面统计即可。以此类推一直到 a n a_n an

比赛中只有想到用 d p [ i ] [ j ] dp[i][j] dp[i][j]为集合中最小值为 i i i ,集合元素和为 j j j的方法数,但时间复杂度不允许。

也是第一次碰到这种退背包的思想。与此同时还有多重背包的退背包。

代码:

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int N=300+20;
const int mod=1e9+7;
int dp[305*505],w[305];
int main()
{
    int t,n,ans,sum;
    scanf("%d",&t);
    while(t--)
    {
        ans=sum=0;
        scanf("%d",&n);
        dp[0]=1;
        for(int i=1;i<=n;++i) {
            scanf("%d",&w[i]);
        }
        sort(w+1,w+n+1);
        for(int i=1;i<=n;++i){
            sum+=w[i];
            for(int j=sum;j>=w[i];--j){
                    dp[j]=(dp[j]+dp[j-w[i]])%mod;
            }
        }
        for(int i=1;i<=n;++i)
        {
            for(int j=w[i];j<=sum;++j) dp[j]=(dp[j]-dp[j-w[i]]+mod)%mod;
            int down=max((sum+1)/2 - w[i],0),up=(sum-w[i])/2;

            for(int j=down;j<=up;++j){
                ans=(ans+dp[j])%mod;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

L:Digit sum

定位:简单题

题意:给出n和b,求 [ 1 , n ] [1,n] [1,n]所有的数在b进制下的数位和。 n ∈ [ 1 , 1 e 6 ] , b ∈ [ 2 , 10 ] n\in [1,1e6],b\in[2,10] n[1,1e6],b[2,10]

思路:我们只要算出每位的每个数对答案的贡献即可。算贡献的方法:假设n在b进制表示为 a k a k − 1 a k − 2 . . . a 0 ​ a_ka_{k-1}a_{k-2}...a_0​ akak1ak2...a0

那么对于下标为 i i i,其所有情况的贡献为 p r e [ i ] ∗ ∑ i = 0 b − 1 ∗ b i + ∑ i = 0 a i − 1 ∗ b i + a i ∗ ( s u f [ i ] + 1 ) pre[i]*\sum _{i=0} ^{b-1}*b^i+\sum _{i=0} ^{a_i-1}*b^i+a_i*(suf[i]+1) pre[i]i=0b1bi+i=0ai1bi+ai(suf[i]+1) 。其中 p r e [ i ] pre[i] pre[i]等于 a k a k − 1 . . . a i + 1 a_ka_{k-1}...a_{i+1} akak1...ai+1 s u f [ i ] suf[i] suf[i]表示为 a i − 1 . . . a 0 a_{i-1}...a_0 ai1...a0

当然也可以数位dp。

#include <bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
ll num[45],pre[45],suf[45];
ll init(ll n,ll b)
{
    ll nn=n;
    ll top=0;
    do{
        num[top++]=n%b;
        n/=b;
    }
    while(n);
    suf[0]=0;
    ll m=1;
    for(ll i=1;i<top;++i){
        suf[i]=suf[i-1]+m*num[i-1];
        m*=b;
    }
    m=nn;
    for(ll i=0;i<top;++i){
        m/=b;
        pre[i]=m;
    }
    return top;
}
ll qpow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a;
        a=a*a;
        b>>=1;
    }
    return ans;
}
ll work(ll n,ll b)
{
    ll top=init(n,b);
    ll ans=0;
    for(ll i=top-1;i>=0;--i)
    {
        ll m=0;
        m+=pre[i]*((b-1)*b)/2*qpow(b,i);
        if(num[i]!=0){
            m+=qpow(b,i)*((num[i]-1)*num[i])/2;
        }
        m+=num[i]*(suf[i]+1);
        ans+=m;
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    ll t,cas=0;
    cin>>t;
    while(t--)
    {
        ll n,b;
        cin>>n>>b;
        cout<<"Case #"<<++cas<<": "<<work(n,b)<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本火锅店点餐系统采用Java语言和Vue技术,框架采用SSM,搭配Mysql数据库,运行在Idea里,采用小程序模式。本火锅店点餐系统提供管理员、用户两种角色的服务。总的功能包括菜品的查询、菜品的购买、餐桌预定和订单管理。本系统可以帮助管理员更新菜品信息和管理订单信息,帮助用户实现在线的点餐方式,并可以实现餐桌预定。本系统采用成熟技术开发可以完成点餐管理的相关工作。 本系统的功能围绕用户、管理员两种权限设计。根据不同权限的不同需求设计出更符合用户要求的功能。本系统中管理员主要负责审核管理用户,发布分享新的菜品,审核用户的订餐信息和餐桌预定信息等,用户可以对需要的菜品进行购买、预定餐桌等。用户可以管理个人资料、查询菜品、在线点餐和预定餐桌、管理订单等,用户的个人资料是由管理员添加用户资料时产生,用户的订单内容由用户在购买菜品时产生,用户预定信息由用户在预定餐桌操作时产生。 本系统的功能设计为管理员、用户两部分。管理员为菜品管理、菜品分类管理、用户管理、订单管理等,用户的功能为查询菜品,在线点餐、预定餐桌、管理个人信息等。 管理员负责用户信息的删除和管理,用户的姓名和手机号都可以由管理员在此功能里看到。管理员可以对菜品的信息进行管理、审核。本功能可以实现菜品的定时更新和审核管理。本功能包括查询餐桌,也可以发布新的餐桌信息。管理员可以查询已预定的餐桌,并进行审核。管理员可以管理公告和系统的轮播图,可以安排活动。管理员可以对个人的资料进行修改和管理,管理员还可以在本功能里修改密码。管理员可以查询用户的订单,并完成菜品的安排。 当用户登录进系统后可以修改自己的资料,可以使自己信息的保持正确性。还可以修改密码。用户可以浏览所有的菜品,可以查看详细的菜品内容,也可以进行菜品的点餐。在本功能里用户可以进行点餐。用户可以浏览没有预定出去的餐桌,选择合适的餐桌可以进行预定。用户可以管理购物车里的菜品。用户可以管理自己的订单,在订单管理界面里也可以进行查询操作。
Sure, I'd be happy to give you some ideas for organizing a speech contest. Here are some suggestions: 1. Determine the theme and rules: Decide on the theme of the contest and the rules for participants. Will it be an open topic, or will there be a specific theme? What is the maximum length of the speech? Will there be any specific guidelines for language or content? 2. Decide on the judging criteria: Determine how the speeches will be evaluated. Will judges be looking for content, delivery, or both? Will there be a score sheet or rubric that judges will use to score the speeches? 3. Recruit judges: Find people who are qualified to judge the speeches. Ideally, they should have experience in public speaking or have a background in the theme of the contest. 4. Promote the contest: Advertise the contest to potential participants, such as students, professionals, or members of a specific community. Use social media, flyers, and other methods to get the word out. 5. Registration and selection: Set a deadline for registration and selection of participants. Consider having a preliminary round to narrow down the field before the final competition. 6. Prepare the venue: Ensure that the venue is suitable for the contest. Make sure that there is adequate seating, sound equipment, and lighting for the speakers. 7. Hold the contest: Set a date and time for the contest, and make sure that all participants and judges are aware of the schedule. Encourage audience participation and provide refreshments. 8. Award ceremony: After the contest, hold an award ceremony to recognize the winners and participants. Provide certificates or other prizes to the top performers. I hope these ideas help you in organizing a successful speech contest!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值