2019ICPC南昌

2019ICPC南昌 重现


待补


B. A Funny Bipartite Graph

题解


C. And and Pair

题意:

给定一个很大的数n的二进制表示s(也就是01串),计算满足条件的数对<i,j>的数量
条件:
0<=j<=i<=n
i&n=i
i&j=0

其中&符号表示位运算与
答案对1e9+7取模

数据范围:|s|<=1e5

解法:

很容易看出可以用数位dp来解
当s[i]=1的时候,i可以为0或者1,i=0时j=0/1,i=1时j=0
当s[i]=0的时候,i只能为0,i=0时j=0/1
当存在最高位限制的时候,i=0时j=0,此时j不能为1
详见代码

需要注意的点是平常数位dp是从高位到低位dp,拆分数的时候一般把高位放在数组的右边
但是给定的01串高位在左边,低位在右边,需要改一下dp的顺序,或者将串翻转一下。

ps:
似乎还有其他解法,组合数学什么的

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
const int mod=1e9+7;
char s[maxm];
int d[maxm];
int dfs(int len,int limit){//limit是i的limit,j不需要limit
    if(!len)return 1;
    if(!limit&&d[len]!=-1)return d[len];
    int ans=0;
    if(s[len]==1){//i可0可1
        if(limit){//10+limit,00+!limit,不能01
            ans+=dfs(len-1,1)+dfs(len-1,0);
        }else{//10,00,01
            ans+=dfs(len-1,0)*3;
        }
    }else{//i只能0
        if(limit){//00+limit,不能01
            ans+=dfs(len-1,1);
        }else{//00,01
            ans+=dfs(len-1,0)*2;
        }
    }
    ans%=mod;
    if(!limit)d[len]=ans;
    return ans;
}
int solve(){
    int len=strlen(s+1);
    reverse(s+1,s+1+len);//翻转一下,否则dp中串的访问顺序就反了
    for(int i=1;i<=len;i++){//init
        d[i]=-1;
        s[i]-='0';
    }
    return dfs(len,1);
}
signed main(){
    int T;cin>>T;
    while(T--){
        scanf("%s",s+1);
        int ans=solve();
        cout<<ans<<endl;
    }
    return 0;
}

E. Bob’s Problem

题意:

给定n个点m条边的无向图,和一个整数k
每条边是黑色或者白色,且每条边有边权
现在你必须在图中选择一些边,最多选择k条白边,
要求选择的边能使得图连通
问选择出的边的最大边权和是多少
如果无解则输出-1

数据范围:n<=5e4,m<=5e5

解法:

要使得边权和最大,因为黑边没有数量限制所以全选,并查集维护一下连通性,把白边存下来

然后白边按边权从大到小排序
遍历白边,如果白边两端不连通则选定且k-=1,否则先不选
如果k次机会选完了还是不连通则无解
如果连通之后k还有剩余,则优先把剩下的边权大的选下来

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e6+5;
struct E{
    int x,y,w;
    friend bool operator<(E a,E b){
        return a.w>b.w;
    }
};
vector<E>temp;
int mark[maxm];//表示被选定的白边
int pre[maxm];
int n,m,k;
int ffind(int x){
    return pre[x]==x?x:pre[x]=ffind(pre[x]);
}
signed main(){
    int T;cin>>T;
    while(T--){
        cin>>n>>m>>k;
        temp.clear();
        for(int i=1;i<=n;i++){
            pre[i]=i;
        }
        int ans=0;
        for(int i=1;i<=m;i++){
            int x,y,w,c;cin>>x>>y>>w>>c;
            if(c==0){//黑边全选
                ans+=w;
                pre[ffind(x)]=ffind(y);//并查集维护图的连通性
            }else{//白边存下来
                temp.push_back({x,y,w});
            }
        }
        sort(temp.begin(),temp.end());
        int len=temp.size();
        for(int i=0;i<len;i++){
            mark[i]=0;
        }
        for(int i=0;i<len&&k;i++){
            int x=ffind(temp[i].x);
            int y=ffind(temp[i].y);
            if(x!=y){
                pre[x]=y;
                ans+=temp[i].w;
                mark[i]=1;
                k--;
            }
        }
        if(k){//如果k还有剩余,则贪心的把边权大的未选的选下
            for(int i=0;i<len&&k;i++){
                if(!mark[i]){
                    ans+=temp[i].w;
                    k--;
                }
            }
        }
        int cnt=0;
        for(int i=1;i<=n;i++){
            if(pre[i]==i){
                cnt++;
                if(cnt>1)break;
            }
        }
        if(cnt!=1){
            puts("-1");
        }else{
            cout<<ans<<endl;
        }
    }
    return 0;
}

G. Eating Plan

题意:

给定长度为n的排列a,其中a[i]=k表示第i个盘子中的食物重量为(k的阶乘)

你的胃很奇怪,当吃了重量为x的食物的时候,剩下的食物质量为x%998857459

现在有m次询问,每次询问给出一个整数k,(保证1<=k<998857459)
你只能吃一个连续区间[L,R]的盘子中的食物,问使得胃中剩下的食物大于等于k的最短区间长度是多少

数据范围:n<=1e5,m<=1e4

解法:

一个简单的想法是枚举区间,计算区间和(用前缀和做),然后对同一区间长度能保留的最大重量取max,
如果存在区间长度为len1,最多保留v1,区间长度len2,最多保留v2,
如果len1<len2且v1>=v2,那么区间len1肯定更优,跳过区间len2
从1到n枚举区间长度,同时记录前缀max,将没有被跳过的区间存到数组e中,显然e[]的区间长度len和值v都是单调递增的
离线记录询问,对询问按k值从小到大排序,用离线算法计算答案就行了(具体操作见代码)

但是n的大小最大1e5,枚举区间不可行
这题的关键信息是模数998857459是一个合数(如果没发现的话,这题直接凉了)
因此当fac[x]中的x到达一定大小的时候会变为0,那么fac[x+1]=fac[x]*(x+1)也会变成0
也就是说x到达某个数之后,后面的阶乘取模之后都是0,打表发现当x>=2803的时候,都是0
那么枚举区间端点的时候,没必要枚举0的位置,
因为给定的a[]是一个排列,那么非0的位置最多2802个,这个大小可以直接O(n2)枚举
解决了区间枚举问题之后,其他的按照前面说的做法做就行了

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
const int mod=998857459;
int fac[maxm];//阶乘
int sum[maxm];//a[i]前缀和
int pos[maxm];//存非0的位置
int ma[maxm];//ma[i]表示模mod意义下,长度为i的区间能吃出的最大值
int ans[maxm];
int a[maxm];
int n,m;
struct Q{//离线存询问
    int k,id;
    friend bool operator<(Q a,Q b){
        return a.k<b.k;
    }
}q[maxm];
struct E{//存区间
    int v,len;
}e[maxm];
signed main(){
    fac[0]=1;
    for(int i=1;i<maxm;i++){//fac[>=2803]=0
        fac[i]=fac[i-1]*i%mod;
    }
    cin>>n>>m;
    int cnt=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        a[i]=fac[a[i]];
        if(a[i]){
            pos[++cnt]=i;
        }
    }
    for(int i=1;i<=n;i++){//前缀和
        sum[i]=(sum[i-1]+a[i])%mod;
    }
    for(int i=1;i<=cnt;i++){//cnt<=2802
        for(int j=i;j<=cnt;j++){
            int t=((sum[pos[j]]-sum[pos[i]-1])%mod+mod)%mod;//[pos[i],pos[j]]的和%mod
            int len=pos[j]-pos[i]+1;
            ma[len]=max(ma[len],t);//更新长度为len的区间所能吃到的最大值
        }
    }
    //预处理最优区间
    int lene=0;
    int maa=0;
    for(int i=1;i<=n;i++){
        if(ma[i]<=maa)continue;//如果ma[i]<=maa,那么选长度小的区间更优,因此直接跳过
        maa=ma[i];
        e[++lene]={ma[i],i};
    }
    for(int i=1;i<=m;i++){//离线存询问
        int k;cin>>k;
        q[i]={k,i};
    }
    sort(q+1,q+1+m);//对询问按k从小到大排序
    //
    int k=1;
    for(int i=1;i<=m;i++){
        while(k<=lene&&e[k].v<q[i].k){
            k++;
        }
        if(k>lene){
            ans[q[i].id]=-1;
        }else{
            ans[q[i].id]=e[k].len;
        }
    }
    for(int i=1;i<=m;i++){
        cout<<ans[i]<<endl;
    }
    return 0;
}

K.Tree

题意:

在这里插入图片描述

解法:

dsu on tree
每个权值开一个线段树
枚举lca
在这里插入图片描述
参考:this

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
vector<int>g[maxm];
int sz[maxm],son[maxm],dep[maxm];
int L[maxm],R[maxm],rk[maxm],idx;
int mark[maxm];
int a[maxm];
int n,k;
int ans;
//
int lc[maxm<<5],rc[maxm<<5],cnt[maxm<<5];
int rt[maxm],tot;//rt[i]表示权值为i的线段树
void update(int x,int val,int l,int r,int &k){
    if(!k)k=++tot;
    cnt[k]+=val;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(x<=mid)update(x,val,l,mid,lc[k]);
    else update(x,val,mid+1,r,rc[k]);
}
int ask(int st,int ed,int l,int r,int k){
    if(!k)return 0;
    if(st<=l&&ed>=r)return cnt[k];
    int mid=(l+r)/2;
    int ans=0;
    if(st<=mid)ans+=ask(st,ed,l,mid,lc[k]);
    if(ed>mid)ans+=ask(st,ed,mid+1,r,rc[k]);
    return ans;
}
//
void dfs1(int x){
    L[x]=++idx;
    rk[idx]=x;
    sz[x]=1;
    for(int v:g[x]){
        dep[v]=dep[x]+1;
        dfs1(v);
        sz[x]+=sz[v];
        if(sz[v]>sz[son[x]]){
            son[x]=v;
        }
    }
    R[x]=idx;
}
void solve(int x,int kep){
    for(int v:g[x]){//solve not son.
        if(v==son[x])continue;
        solve(v,0);
    }
    if(son[x]){
        solve(son[x],1);
    }
    for(int v:g[x]){
        if(v==son[x])continue;
        for(int i=L[v];i<=R[v];i++){
            int id=rk[i];
            int need_val=2*a[x]-a[id];
            if(need_val>=0&&need_val<=n){
                int maxdep=min(n,k+2*dep[x]-dep[id]);
                ans+=ask(1,maxdep,1,n,rt[need_val]);
            }
        }
        for(int i=L[v];i<=R[v];i++){
            int id=rk[i];
            update(dep[id],1,1,n,rt[a[id]]);
        }
    }
    update(dep[x],1,1,n,rt[a[x]]);//上面只加了x的所有子节点,但是没有加x,这里补上
    if(!kep){//not kep
        for(int i=L[x];i<=R[x];i++){
            int id=rk[i];
            update(dep[id],-1,1,n,rt[a[id]]);
        }
    }
}
signed main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=2;i<=n;i++){
        int fa;cin>>fa;
        g[fa].push_back(i);
    }
    dep[1]=1;
    dfs1(1);
    solve(1,1);
    ans*=2;//ordered
    cout<<ans<<endl;
    return 0;
}

L.Who is the Champion

题意:

签到题,告诉你球赛的计分规则,要求计算出球赛的冠军,模拟一下就行了

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e3+5;
int a[maxm][maxm];
int dif[maxm];
int p[maxm];
signed main(){
    int n;cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            if(a[i][j]>a[j][i]){
                p[i]+=3;
            }else if(a[i][j]==a[j][i]){
                p[i]++;
                p[j]++;
            }else{
                p[j]+=3;
            }
            dif[i]+=a[i][j]-a[j][i];
            dif[j]+=a[j][i]-a[i][j];
        }
    }
    int pma=-1;
    for(int i=1;i<=n;i++){
        pma=max(pma,p[i]);
    }
    int ma=-1;
    int cnt=0;
    for(int i=1;i<=n;i++){
        if(p[i]==pma){
            if(ma==-1){
                ma=i;
                cnt=1;
            }else{
                if(dif[i]>dif[ma]){
                    ma=i;
                    cnt=1;
                }else if(dif[i]==dif[ma]){
                    cnt++;
                }
            }
        }
    }
    if(cnt!=1||ma==-1){
        puts("play-offs");
    }else{
        cout<<ma<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com
UIUC ICPC Spring Coding Contest 2019是UIUC(伊利诺伊大学厄巴纳-香槟分校)举办的一个编程比赛。UIUC ICPC Spring Coding Contest 2019是ACM国际大学生程序设计竞赛(ACM International Collegiate Programming Contest)的一部分。ACM国际大学生程序设计竞赛是世界上最具影响力的大学生计算机竞赛之一,每年吸引了来自全球各地的大学生参与。这个比赛旨在培养学生的算法和编程技能,提供一个展示和交流的平台。参赛者需要在规定时间内解决一系列编程问题。 参加UIUC ICPC Spring Coding Contest 2019对于那些对算法和编程有兴趣的学生来说,是一个很好的学习和锻炼机会。比赛中的问题通常涉及各种算法和数据结构,要求参赛者能够用编程语言实现有效和高效的解决方案。参赛者可以通过解决问题来提高他们的算法和编程技能,并与其他参赛者交流和学习。 在准备UIUC ICPC Spring Coding Contest 2019之前,建议参赛者先掌握一些基本的编程知识和技能,如数据结构、算法、编程语言等。参赛者可以参考一些相关的教程和学习资料,如GeeksforGeeks和HackerEarth等网站提供的编程教程。此外,还可以参考一些竞赛经验分享的文章和博客,了解其他人是如何准备和参加编程比赛的。 总之,参加UIUC ICPC Spring Coding Contest 2019是一个很好的机会,可以提高算法和编程技能,与其他参赛者交流和学习。准备比赛前,建议参赛者掌握基本的编程知识和技能,并参考一些相关的教程和学习资料。祝你在比赛中取得好成绩!<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Awesome Competitive Programming Awesome](https://blog.csdn.net/qq_27009517/article/details/86593200)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值