The 2024 ICPC Asia Shanghai Regional Contest, Early Access

tot:这场vp写了两题罚时272,上海的场还是炸呀,差点就是我们队去了。。赛后补了3题,共5题。这场铜是从3题448到5题907,最后一银是5题888。第三题赛时的时候写复杂了一点,并且删边的部分是不对的,赛时也没有留意到。第四题没有看题解赛后补了,存在一点问题(ceil和floor精度不够--真的不要用了),然后有一个符号写错了(这个对着式子也容易找到错)。第五题一样不难,就是那种知道得想的细一点,但是又想的不够细的题,导致一直不对,然后一直对着样例改。最后对拍出样例接着改才过的(得需要两个人一起想想各种细节吧),总体真不难这题,但是也不是说很简单,feel吧。

The 2024 ICPC Asia Shanghai Regional Contest, Early Access

I. In Search of the Ultimate Artifact

思路:我实现wa了几发这个签到题,还怀疑是不是取模后最大,但是不可能过这么多人。然后yk再写了一发ac了。我的写的那发取模取少了又。。

const int mod=998244353;
const int N=1e6+10;
ll n;
ll a[N];
void solve(){
    ll k; cin >> n >> k;
    for(int i=1;i<=n;i++) a[i]=0;
    for(int i=1;i<=n;i++){
        cin >> a[i];
        if(!a[i]) i--,n--;
    }
    if(!n){cout << "0\n"; return ;}
    sort(a+1,a+n+1,greater<>());
    ll ans=a[1]%mod;
    for(int i=2;i<=n-k+2;i++){
        for(int j=i;j<i+k-1;j++) (ans*=a[j])%=mod;
        i+=k-2;
    }
    cout << ans << "\n";
}

C. Conquer the Multiples

思路:容易发现拿奇数的人是可以拿偶数字的,但是拿偶数字的人是不可能拿到奇数字的。那么只需要判断拿奇数字的人能不能把偶数字的人的’路‘拦截即可。

int l,r;
void solve(){
    cin>>l>>r;
    if(l==1||r-l+1==1){ cout<<"Alice"<<endl; return; }
    if(r%2==1&&l%2==1){ cout<<"Alice"<<endl; return; }
    if(r%2==1&&l%2==0){ cout<<"Bob"<<endl; return; }
    if(l%2==1){
        if(l*2<=r){ cout<<"Alice"<<endl; return; }
        else { cout<<"Bob"<<endl; return; }
    }
    else{
        if((l+1)*2<=r){ cout<<"Bob"<<endl; return; }
        else { cout<<"Alice"<<endl; return; }
    }
}

B. Basic Graph Algorithm

思路:不难的题,不能退出也不能往下一步走才建边,否则能退出就退出,细节见注释。

int n,m;
int arr[300005],nex=1;
vector<int> vct[300005];
set<int> st[300005];
vector<pair<int,int>> ans;
void dfs(int u){
    //这里会有一个非常非常不好发现的错误!!如果没有vct再存一次图,只是用set存图的话.
    //一旦x在y前先遍历,那么y会删除到x这条边,但是x不会删除到y这条边(因为y到x已经没边了).
    //如果此时y不是被x遍历到的,那么将可能会多建边.
    //即x应该要return才对的,但是x没有return,因为还存有到y的边,但是实际上y已经被遍历完了.
    for(auto v:vct[u]) st[v].erase(u);
    while(!st[u].empty()&&nex<=n){
        if(st[u].count(arr[nex])){
            st[u].erase(arr[nex]);
            nex++,dfs(arr[nex-1]);
        }
        else{
            ans.eb(u,arr[nex]);
            nex++,dfs(arr[nex-1]);
        }
    }
}
void solve(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v; cin>>u>>v;
        //无自环,有重边.
        //需要再建一个不做删减的图,用作给st删边.如果单纯用st作为删边的话,会有问题!!并且很难发现这个问题..
        /*if(!st[u].count(v))*/ vct[u].eb(v),vct[v].eb(u);
        st[u].ep(v),st[v].ep(u);
    }
    for(int i=1;i<=n;i++) cin>>arr[i];
    while(nex<=n) nex++,dfs(arr[nex-1]);
    cout<<sz(ans)<<endl;
    for(auto a:ans) cout<<a.first<<" "<<a.second<<endl;
}
//wa on:
//10 20
//5 6
//4 6
//5 10
//5 4
//9 3
//7 10
//2 10
//3 10
//3 6
//9 7
//9 5
//4 2
//2 8
//10 2
//5 3
//4 6
//5 10
//6 7
//2 5
//8 9
//10 7 5 6 4 2 8 9 3 1

写复杂了的:

int n,m;
vector<int> vct[300005];
vector<int> arr(300005);
vector<int> pos(300005);
vector<int> cnt(300005);
vector<pair<int,int>> ans;
map<pair<int,int>,bool> mp;
vector<bool> vis(300005);
vector<pair<int,int>> itv;
vector<int> len;
int l0,r0,idx=0,cntt=0,nex=1,begin0;
void dfs0(int u){
    l0=min(l0,pos[u]);
    r0=max(r0,pos[u]);
    vis[u]=true;
    for(auto v:vct[u]){
        if(!vis[v]) dfs0(v);
    }
}
void dfs(int u){
    cntt++;
    for(auto v:vct[u]) cnt[v]++;  key::当每一个点被vis,把相邻的点cnt[]++;
    if(cntt==len[idx]||nex>n) return;
    if(cnt[u]==sz(vct[u])&&u!=begin0) return;
    while(cnt[u]<vct[u].size()||u==begin0){
        if(cntt==len[idx]||nex>n) return;
        int mi=min(u,arr[nex]);
        int mx=max(u,arr[nex]);
        if(mp[{mi,mx}]){
            cnt[u]++,cnt[arr[nex]]++;  这行是不对的!!
            nex++;
            dfs(arr[nex-1]);
        }
        else{
            ans.eb(u,arr[nex]);
            nex++;
            dfs(arr[nex-1]);
        }
    }
}

void solve(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v; cin>>u>>v;
        vct[u].eb(v);
        vct[v].eb(u);
        mp[{min(u,v),max(u,v)}]=true;
    }
    for(int i=1;i<=n;i++){
        cin>>arr[i];
        pos[arr[i]]=i;
    }
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            l0=INT_MAX,r0=INT_MIN;
            dfs0(i);
            itv.eb(l0,r0);
        }
    }
    sort(itv.begin(),itv.end());
//    for(auto i:itv) cout<<i.first<<" "<<i.second<<endl;
//    cout << endl;
    pair<int,int> e=itv[0];
    for(int i=1;i<sz(itv);i++){
        if(e.second>=itv[i].first) e.second=max(itv[i].second, e.second);  key:这里得取max!!!
        else len.eb(e.second-e.first+1),e=itv[i];
    }
    len.eb(e.second-e.first+1);
//    for(auto l:len) cout<<l<<' ';
    nex=1;
    for(idx=0;idx<sz(len);idx++) {
        cntt=0,begin0=arr[nex],nex++;
        dfs(begin0);
    }
    cout<<sz(ans)<<endl;
    for(auto a:ans) cout<<a.first<<" "<<a.second<<endl;
}

G. Geometry Task

思路:经典二分中位数,然后check是贪心的,还需要一个前缀数组和后缀数组.

按照a<0和a>0先处理出neg数组和pst数组;-用作后续!双指针!!贪心!处理出pre和suf的,主要看样例2.

c数组排序后.pre[i]定义为从1-->i最多可以匹配多少对;suf[i]定义为从n-->i最多可以匹配多少对.

然后a==0的特殊计算一下,最后枚举1-->n,取出max即可知道,在当前中位数x下,最多可以匹配多少对大于等于x的对数.

主要注意实现的细节,并且!!ceil和floor的精度是有问题的!!!不要用!!老老实实自己分类讨论一下!!

int n;
pair<int,int> line[100005];
int c[100005];
vector<int> pre(100005);
vector<int> suf(100005);
vector<int> neg,pst;
bool check(int x){            //贪心,分类讨论.
    neg.clear(),neg.eb(0);
    pst.clear(),pst.eb(0);
    pre[0]=0,suf[n+1]=0; //init!
    int cnt=0,mx=0;
    for(int i=1;i<=n;i++){
        int a=line[i].first,b=line[i].second;
        if(a==0&&b>=x) cnt++;
        else if(a>0){
//            pst.eb((int)ceill((x-b)*1.0/a));  //精度有问题..非必要不用和浮点数有关的函数!!
            if(x-b>=0){
                if((x-b)%a==0) pst.eb((x-b)/a);
                else pst.eb((x-b)/a+1);
            }
            else pst.eb((x-b)/a);
        }
        else if(a<0){
//            neg.eb((int)floorl((x-b)*1.0/a));
            if(x-b>=0){
                if((x-b)%a==0) neg.eb((x-b)/a);
                else neg.eb((x-b)/a-1);
            }
            else neg.eb((x-b)/a);
        }
    }
    int n1=sz(neg)-1,n2=sz(pst)-1;
    sort(neg.begin()+1,neg.begin()+n1+1);
    sort(pst.begin()+1,pst.begin()+n2+1);
    int idx=1;
    for(int i=1;i<=n1&&idx<=n;i++){  //neg--pre
        pre[idx]=pre[idx-1];
        if(neg[i]==0){
            if(c[idx]==0) pre[idx]++,idx++;
            else if(c[idx]<0) pre[idx]++,idx++;
            else if(c[idx]>0) continue;
        }
        else if(neg[i]<0){
            if(c[idx]==0) continue;
            else if(c[idx]<0){
                if(abs(neg[i])<=abs(c[idx])) pre[idx]++,idx++;
            }
            else if(c[idx]>0) continue;
        }
        else if(neg[i]>0){
            if(c[idx]==0) pre[idx]++,idx++;
            else if(c[idx]<0) pre[idx]++,idx++;
            else if(c[idx]>0){
                if(neg[i]>=c[idx]) pre[idx]++,idx++;  //这里是>= 不是<=
            }
        }
    }
    for(int i=idx;i<=n;i++) pre[i]=pre[i-1];
    idx=n;
    for(int i=n2;i>=1&&idx>=1;i--){  //pst--suf
        suf[idx]=suf[idx+1];
        if(pst[i]==0){
            if(c[idx]==0) suf[idx]++,idx--;
            else if(c[idx]<0) continue;
            else if(c[idx]>0){
                if(c[idx]>=pst[i]) suf[idx]++,idx--;
            }
        }
        else if(pst[i]<0){
            if(c[idx]==0) suf[idx]++,idx--;
            else if(c[idx]<0){
                if(pst[i]<=c[idx]) suf[idx]++,idx--;
            }
            else if(c[idx]>0) suf[idx]++,idx--;
        }
        else if(pst[i]>0){
            if(c[idx]==0) continue;
            else if(c[idx]<0) continue;
            else if(c[idx]>0){
                if(pst[i]<=c[idx]) suf[idx]++,idx--;
            }
        }
    }
    for(int i=idx;i>=1;i--) suf[i]=suf[i+1];
    for(int i=1;i<=n;i++) mx=max(mx,pre[i]+suf[i]);
    return cnt+mx>=(n+1)/2;
}
//经典二分中位数,然后check是贪心的,还需要一个前缀数组和后缀数组.
//按照a<0和a>0先处理出neg数组和pst数组;--用作后续!双指针!!贪心!处理出pre和suf的,主要看样例2.
//c数组排序后.pre[i]定义为从1-->i最多可以匹配多少对;suf[i]定义为从n-->i最多可以匹配多少对.
//然后a==0的特殊计算一下,最后枚举1-->n,取出max即可知道,在当前中位数x下,最多可以匹配多少对大于等于x的对数.
//主要注意实现的细节,并且!!ceil和floor的精度是有问题的!!!不要用!!老老实实自己分类讨论一下!!
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>line[i].first;
    for(int i=1;i<=n;i++) cin>>line[i].second;
    for(int i=1;i<=n;i++) cin>>c[i];
    if(n==1){ cout<<line[1].first*c[1]+line[1].second<<endl; return; }
    sort(c+1,c+n+1);
    int l=-2e18,r=2e18,ans=0;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)){
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    cout<<ans<<endl;
}

D. Decrease and Swap

思路:对着样例和对拍器改出来的。

合法情况,末四位如下:
x,x,0,0
18,18,x,x
18,x,18,0
关键细节(按我那种实现have是判不到的,得在其中特判):
初级:010101--这种是可以的,str[4]把str[6]拖回来一次,然后str[2]把str[4]拖到str[3],str[3]再把str[5]拖到str[4].
中级:011001--初级的变式,也是可以的.
高级:111010011100001--更是变式,也是可以的.
//往右推完之后是,101010101010101.
但是在判的时候,来到str[n-4],have=3,然后去判str[i+2]和str[i+1]是否为'1',然后都不是.
然后再往后判到i==n-2:have已经不足2了,cc也为false.导致结果为No.
key:但是实际上,这个推完之后是变成了初级的样子的,所以最后还要判have>=3也是合法的!!

晕头。。

int n;
char str[1000006];
//合法情况,末四位如下:
//x,x,0,0
//18,18,x,x
//18,x,18,0
关键细节(按我那种实现have是判不到的,得在其中特判):
初级:010101--这种是可以的,str[4]把str[6]拖回来一次,然后str[2]把str[4]拖到str[3],str[3]再把str[5]拖到str[4].
中级:011001--初级的变式,也是可以的.
高级:111010011100001--更是变式,也是可以的.
//往右推完之后是,101010101010101.
但是在判的时候,来到str[n-4],have=3,然后去判str[i+2]和str[i+1]是否为'1',然后都不是.
然后再往后判到i==n-2:have已经不足2了,cc也为false.导致结果为No
key:但是实际上,这个推完之后是变成了初级的样子的,所以最后还要判have>=3也是合法的!!
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>str[i];
    if(str[n]=='0'&&str[n-1]=='0'){ cout<<"Yes"<<endl; return; }
    if(n<=3){ cout<<"No"<<endl; return; }
    if(str[n-2]=='1'&&str[n-3]=='1'){ cout<<"Yes"<<endl; return; }
    if(str[n]=='0'&&str[n-3]=='1'){ cout<<"Yes"<<endl; return; }
    if(str[n]=='1'&&str[n-1]=='0'){  //0,18 需要str[n-2],str[n-3]同时为18.
        int have=0,cc=0;
        for(int i=1;i<=n-2;i++){
            if(str[i]=='1') have++;
            else if(have>0) have--;
            if(i==n-4&&have>0&&(str[i+2]=='1'||str[i+1]=='1'||have>=3)) cc=1;  //最后加了have>=3
//            if(i==n-4&&str[i]=='1'&&str[i+1]=='1') cc=1; //睡醒之后开始想能怎么wa,突然想到这个:011001--加之前wa40多,改后wa190多..
        }
        if(have>=2||cc) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    else{  //18,0 和 18,18 只需要str[n-3]为18,str[n-2]都行.
        int have=0,cc=0;
        for(int i=1;i<=n-3;i++){
            if(str[i]=='1') have++;
            else if(have>0) have--;
            if(i==n-4&&have>0&&(str[i+2]=='1'||str[i+1]=='1'||have>=3)) cc=1;
        }
        if(have>=1||cc) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
}
//没想到一种最nb的操作:10101--这个时候应该选择str[3],把最后的1换出来,再选择str[1],使得str[3]回退一步到达合适的位置.
//1
//15
//111010011100001
//细中细啊..

### 2024 ICPC Asia Regional Information #### 赛事概述 国际大学生程序设计竞赛(International Collegiate Programming Contest, ICPC)是一项全球性的编程比赛,旨在促进计算机科学教育并培养团队合作精神。每年的亚洲区域赛吸引了来自多个国家和地区的学生参与[^1]。 #### 时间安排 通常情况下,ICPC亚洲区域赛会在每年秋季举行预选赛,并于冬季举办现场决赛。具体到2024年的赛事时间表尚未公布,但可以参考往届的时间节点来推测大致的日程安排[^3]。 #### 参赛资格 参赛队伍一般由三名在校本科生组成,在指导教师的带领下完成一系列算法挑战题目。每支队伍需通过线上选拔赛获得参加线下总决赛的机会。对于有兴趣报名的同学来说,建议关注所在学校的通知以及官方渠道发布的最新消息[^4]。 #### 报名方式 各高校会组织内部选拔活动以组建代表队;成功入围者将统一注册成为正式选手。需要注意的是,不同赛区可能有不同的截止日期和特殊要求,因此务必提前做好准备并按时提交所需材料[^2]。 #### 备战指南 为了更好地迎接即将到来的比赛,可以从以下几个方面着手准备: - **学习基础知识**:深入理解数据结构、图论、动态规划等核心概念; - **练习历年真题**:熟悉各类题型及其解法模式; - **加强协作能力**:提高沟通效率,学会分工合作解决问题; - **保持良好心态**:面对高强度训练时不急不躁,调整好心理状态。 ```python def prepare_for_icpc(): study_fundamentals() practice_past_papers() enhance_teamwork_skills() maintain_positive_attitude() prepare_for_icpc() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值