The 2024 ICPC Asia East Continent Final Contest

Preface

好久没训练了,一想到现在的水平下周要去打 CCPC Final,感觉冲击 Ag 都成问题啊

这场直接头等战犯,先是开局签到 A 连 WA 四发搞崩罚时,后期模拟 E 怒写半天最后还因为 Corner Case 没过

最后把理论 6 题希望冲 Au 的局打崩了,直接俯冲 Cu 区


A. Hitoshizuku

考虑从小到大考虑每个 \(b_i\) 最小的人,显然让他去匹配两个 \(a_j\le b_i\)\(b_j\) 最小的两个人一定最优

实现的时候最好直接搞几个 set 来维护,我赛时写的双指针有一些奇怪的细节要注意,WA 了好多发

#include<cstdio>
#include<iostream>
#include<set>
#include<array>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=300005;
int t,n,vis[N]; array <int,3> a[N],b[N];
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        scanf("%d",&n); n*=3;
        for (RI i=1;i<=n;++i)
        {
            int x,y; scanf("%d%d",&x,&y);
            a[i]={x,y,i}; b[i]={y,x,i}; vis[i]=0;
        }
        sort(a+1,a+n+1); sort(b+1,b+n+1);
        bool flag=1; RI pa=1,pb=1,cnt=0;
        set <array <int,3>> st;
        vector <array <int,3>> ans;
        while (cnt<n/3)
        {
            while (pb<=n&&vis[b[pb][2]]) ++pb;
            if (pb>n) { flag=0; break; }
            vis[b[pb][2]]=1;
            while (pa<=n&&a[pa][0]<=b[pb][0])
            {
                if (vis[a[pa][2]]) { ++pa; continue; }
                st.insert({a[pa][1],a[pa][0],a[pa][2]});
                ++pa;
            }
            int num=0,x,y;
            while (!st.empty())
            {
                int id=(*st.begin())[2];
                st.erase(st.begin());
                if (vis[id]) continue;
                ++num;
                if (num==1) x=id; else
                if (num==2) { y=id; break; }
            }
            if (num!=2) { flag=0; break; }
            vis[x]=vis[y]=1;
            ans.push_back({b[pb][2],x,y});
            ++cnt;
        }
        if (!flag) { puts("No"); continue; }
        puts("Yes");
        for (auto [x,y,z]:ans) printf("%d %d %d\n",x,y,z);
    }
    return 0;
}

E. Corrupted Scoreboard Log

只能说太久不写代码是这样的,曾经手拿把掐的模拟题现在写的磕磕绊绊,最后还没判 00 的 Corner Case

这题本身没啥思维难度,把整个串分解后,考虑枚举第一题和前面的分界符

剩下每题直接分类讨论一下各种情况即可,注意到每个题的提交次数只有 \(1/2/3\) 位,三位的情况又很特殊可以直接判,因此这部分复杂度是 \(O(2^m)\)

#include<cstdio>
#include<iostream>
#include<string>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
int n,m; string ans; vector <string> vec; vector <int> tp; bool flag;
inline void DFS(CI tot_pro,CI tot_pnt,string ans,CI pro,CI pnt,CI now)
{
    // if (tot_pnt==1322&&now<vec.size()) cout<<vec[now]<<endl;
    // if (tot_pnt==1322) printf("now = %d,tot_pro = %d, tot_pnt = %d , pro = %d, pnt = %d\n",now,tot_pro,tot_pnt,pro,pnt);
    if (now==vec.size())
    {
        if (pro==tot_pro&&pnt==tot_pnt)
        {
            cout<<ans<<endl; flag=1;
        }
        return;
    }
    string tmp_ans=ans;
    if (tp[now]==1) // try case
    {
        if (vec[now]=="1")
        {
            ans+=" 1 try";
            DFS(tot_pro,tot_pnt,ans,pro,pnt,now+1);
            ans=tmp_ans;
            return;
        }
        int cur_pnt=0;
        for (RI j=0;j<vec[now].size()-1;++j)
        cur_pnt=cur_pnt*10+vec[now][j]-'0';
        if (vec[now][0]=='0')
        {
        	if (cur_pnt==0&&vec[now].size()==2)
        	{
		        ans+=' ';
		        for (RI j=0;j<vec[now].size()-1;++j) ans+=vec[now][j];
		        ans+=' '; ans+=vec[now].back(); ans+=" try";
		        DFS(tot_pro,tot_pnt,ans,pro+1,pnt+cur_pnt,now+1);
		        ans=tmp_ans;
        	}
        	return;
        }
        if (cur_pnt>299) return;
        ans+=' ';
        for (RI j=0;j<vec[now].size()-1;++j) ans+=vec[now][j];
        ans+=' '; ans+=vec[now].back(); ans+=" try";
        DFS(tot_pro,tot_pnt,ans,pro+1,pnt+cur_pnt,now+1);
        ans=tmp_ans;
        return;
    }
    if (vec[now][0]!='0') //do not solve this problem
    {
        int try_times=0;
        for (RI j=0;j<vec[now].size();++j)
        try_times=try_times*10+vec[now][j]-'0';
        if (try_times<=100)
        {
            ans+=' ';
            for (RI j=0;j<vec[now].size();++j) ans+=vec[now][j];
            ans+=" tries";
            DFS(tot_pro,tot_pnt,ans,pro,pnt,now+1);
            ans=tmp_ans;
        }
    }
    if (flag) return;
    if (vec[now].size()>=2&&vec[now].back()!='0'&&vec[now].back()!='1') //1 digit
    {
        int cur_pnt=0;
        for (RI j=0;j<vec[now].size()-1;++j)
        cur_pnt=cur_pnt*10+vec[now][j]-'0';
        if (vec[now][0]=='0')
        {
        	if (cur_pnt==0&&vec[now].size()==2)
        	{
	            ans+=' ';
	            for (RI j=0;j<vec[now].size()-1;++j) ans+=vec[now][j];
	            ans+=' '; ans+=vec[now].back(); ans+=" tries";
	            DFS(tot_pro,tot_pnt,ans,pro+1,pnt+cur_pnt+(vec[now].back()-'0'-1)*20,now+1);
	            ans=tmp_ans;
        	}
        } else
        if (cur_pnt<=299)
        {
            ans+=' ';
            for (RI j=0;j<vec[now].size()-1;++j) ans+=vec[now][j];
            ans+=' '; ans+=vec[now].back(); ans+=" tries";
            DFS(tot_pro,tot_pnt,ans,pro+1,pnt+cur_pnt+(vec[now].back()-'0'-1)*20,now+1);
            ans=tmp_ans;
        }
    }
    if (flag) return;
    if (vec[now].size()>=3&&vec[now][vec[now].size()-2]!='0') //2 digits
    {
        int cur_pnt=0;
        for (RI j=0;j<vec[now].size()-2;++j)
        cur_pnt=cur_pnt*10+vec[now][j]-'0';
        int try_times=0;
        for (RI j=vec[now].size()-2;j<vec[now].size();++j)
        try_times=try_times*10+vec[now][j]-'0';
        if (vec[now][0]=='0')
        {
        	if (cur_pnt==0&&vec[now].size()==3)
        	{
	            ans+=' ';
	            for (RI j=0;j<vec[now].size()-2;++j) ans+=vec[now][j];
	            ans+=' ';
	            for (RI j=vec[now].size()-2;j<vec[now].size();++j) ans+=vec[now][j];
	            ans+=" tries";
	            DFS(tot_pro,tot_pnt,ans,pro+1,pnt+cur_pnt+(try_times-1)*20,now+1);
	            ans=tmp_ans;
        	}
        } else
        if (cur_pnt<=299)
        {
            ans+=' ';
            for (RI j=0;j<vec[now].size()-2;++j) ans+=vec[now][j];
            ans+=' ';
            for (RI j=vec[now].size()-2;j<vec[now].size();++j) ans+=vec[now][j];
            ans+=" tries";
            DFS(tot_pro,tot_pnt,ans,pro+1,pnt+cur_pnt+(try_times-1)*20,now+1);
            ans=tmp_ans;
        }
    }
    if (flag) return;
    if (vec[now].size()>=4&&vec[now][vec[now].size()-3]=='1'&&vec[now][vec[now].size()-2]=='0'&&vec[now].back()=='0') // 3 digits (only 100)
    {
        int cur_pnt=0;
        for (RI j=0;j<vec[now].size()-3;++j)
        cur_pnt=cur_pnt*10+vec[now][j]-'0';
        if (vec[now][0]=='0')
        {
        	if (cur_pnt==0&&vec[now].size()==4)
        	{
	            ans+=' ';
	            for (RI j=0;j<vec[now].size()-3;++j) ans+=vec[now][j];
	            ans+=' ';
	            ans+="100 tries";
	            DFS(tot_pro,tot_pnt,ans,pro+1,pnt+cur_pnt+(100-1)*20,now+1);
	            ans=tmp_ans;
        	}
        } else
        if (cur_pnt<=299)
        {
            ans+=' ';
            for (RI j=0;j<vec[now].size()-3;++j) ans+=vec[now][j];
            ans+=' ';
            ans+="100 tries";
            DFS(tot_pro,tot_pnt,ans,pro+1,pnt+cur_pnt+(100-1)*20,now+1);
            ans=tmp_ans;
        }
    }
    if (flag) return;
}
inline void solve(CI tot_pro)
{
    // printf("tot_pro = %d\n",tot_pro);
    for (RI i=1;i<=6;++i)
    {
        string tmp_ans=ans;
        if (i>=vec[0].size()) break;
        string tmp=vec[0];
        // cout<<"tmp = "<<tmp<<endl;
        int tot_pnt=0;
        for (RI j=0;j<tmp.size()-i;++j)
        tot_pnt=tot_pnt*10+tmp[j]-'0';
        if (tmp[0]=='0')
		{
			if (tmp.size()-i!=1) continue;
		}
        vec[0]="";
        for (RI j=tmp.size()-i;j<tmp.size();++j)
        vec[0]+=tmp[j];
        for (RI j=0;j<tmp.size()-i;++j) ans+=tmp[j];
        // cout<<"i = "<<i<<" "<<vec[0]<<endl;
        DFS(tot_pro,tot_pnt,ans,0,0,0);
        if (flag) return;
        vec[0]=tmp;
        ans=tmp_ans;
    }
}
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin>>n>>m;
    for (RI id=1;id<=n;++id)
    {
        vec.clear(); tp.clear(); flag=0;
        string s; cin>>s; string tmp="";
        if (s=="00") { cout<<"0 0"<<endl; continue; }
        for (RI i=0;i<s.size();++i)
        {
            if (s[i]=='t')
            {
                vec.push_back(tmp); tmp="";
                if (s[i+2]=='y') tp.push_back(1),i+=2;
                else tp.push_back(0),i+=4;
                continue;
            }
            tmp+=s[i];
        }
        int d=vec[0][0]-'0';
        vec[0].erase(vec[0].begin());
        ans=""; ans+=char(d+'0'); ans+=' '; solve(d);
        if (flag) continue;
        int dd=vec[0][0]-'0';
        vec[0].erase(vec[0].begin());
        ans=""; ans+=char(d+'0'); ans+=char(dd+'0'); ans+=' '; solve(d*10+dd);
    }
    return 0;
}

F. Boolean Function Reconstruction

被徐神单切力

考虑一种很 trivial 的构造方式,对于所有 \(f(i)=1\) 的状态 \(i\),直接把它对应的与表达式或到最后的答案中即可

更进一步的观察,我们可以发现由于序列中没有取反,因此如果状态 \(f(x)=1\),那么 \(x\) 的所有超集 \(y\) 都满足 \(f(y)=1\),因此可以贪心地构造最小的那些表示状态

但如果直接这么做还是会超过符号限制,因此我们不妨合并一些子项,即按顺序提取出每一个字符,再将剩下的子项按照含有/不含有这个字符分类,递归构造即可

#include <bits/stdc++.h>

namespace check {
    using bitset = std::bitset<1 << 15>;

    bitset val[15];

    bitset eval(const char *&s) {
        if(isalpha(*s)) return val[*s++ - 'a'];

        assert(*s == '(');
        auto val1 = eval(++s);
        const char *p1 = s;
        auto val2 = eval(++s);
        assert(*s++ == ')');

        if(*p1 == '&') return val1 & val2;
        else           return val1 | val2;
    }

    std::string check(int n, std::string s) {
        for(int i = 0; i < n; ++i) for(int j = 0; j < (1 << n); ++j)
            val[i][j] = (j >> i & 1);

        const char *p = s.c_str();
        auto result = eval(p);
        std::stringstream res;
        for(int i = 0; i < (1 << n); ++i) res << result[i];
        return res.str();
    }
}

void generate(int i, const std::vector<int> &s, std::stringstream &ss) {
    std::vector<int> a, b;
    int aa = 0;
    for(auto s: s) {
        if(s >> i & 1) {
            if(s == (1 << i)) aa = 1;
            else         a.push_back(s ^ (1 << i));
        } else           b.push_back(s);
    }
    if((a.size() || aa) && b.size()) ss << '(';
    if(a.size()) ss << '(';
    if(aa || a.size()) ss << char(i + 'a');
    if(a.size()) ss << '&', generate(i + 1, a, ss), ss << ')';
    if((a.size() || aa) && b.size()) ss << '|';
    if(b.size()) generate(i + 1, b, ss);
    if((a.size() || aa) && b.size()) ss << ')';
    return ;
}

void work() {
    int n; std::cin >> n;
    
    std::string input;
    std::vector<bool> c(1 << n); {
        std::cin >> input;
        for(int i = 0; i < (1 << n); ++i) c[i] = (input[i] == '1');
    }

    int op_count = 0;
    int count0 = 0;
    for(int i = 0; i < (1 << n); ++i) count0 += (c[i] == 0);
    if(count0 == 0       ) return std::cout << "Yes\nT\n", void(0);
    if(count0 == (1 << n)) return std::cout << "Yes\nF\n", void(0);

    if(c[0] == 1) return std::cout << "No\n", void(0);

    std::vector<bool> d(1 << n, 0);
    std::vector<int> ans;
    for(int i = 1; i < (1 << n); ++i) {
        if(d[i] && !c[i]) return std::cout << "No\n", void(0);
        if(d[i] == c[i]) continue;

        ans.push_back(i);
        for(int S = ((1 << n) - 1) ^ i; ; S = (((1 << n) - 1) ^ i) & (S - 1)) {
            d[S | i] = 1;
            if(S == 0) break;
        }
    }

    std::cout << "Yes\n";
    std::stringstream anss;
    generate(0, ans, anss);
    std::cout << anss.str() << char(10);
    return ;
}

int main() {
    // freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
    std::ios::sync_with_stdio(false);

    int T; std::cin >> T; while(T--) work();
    fclose(stdin);
    return 0;
}


G. Collatz Conjecture

首先注意到 \([1,B]\) 中的所有数都一定在某个环上

这是因为对于某个 \(x\in [1,B]\),它在加上了若干次 \(B\) 并成为 \(y\times A\) 后,除去 \(A\) 变为的 \(y\) 一定也 \(\in[1,B]\)

不妨假设初始的 \(n\) 在环上,则它一定由某个 \(r\in[1,B]\) 不断加 \(B\) 得来,很容易发现这个 \(r\)\(n\)\(B\) 意义下同余

定下 \(r\) 后判断 \(n\) 是否合法只要找出环上最大的值即可,设进行了 \(x\) 次加 \(B\) 操作,则有 \(r+x\times B=y\times A\),用 exgcd 解出最小的 \(x\) 即可

#include<bits/stdc++.h>
using namespace std;
#define int long long

int ex_gcd(int a, int b, int &x, int &y) {
    if (0 == b) {x = 1; y = 0; return a;}
    int ret = ex_gcd(b, a%b, y, x);
    y -= a / b * x;
    return ret;
}

int calc(int A, int B, int R) { //xA + yB = R 的最小正整数 x
    int x0, y0;
    int g = ex_gcd(A, B, x0, y0);
    int x00 = x0 * (R / g), y00 = y0 * (R / g);
    int x1 = B / g, y1 = A / g;
    return ((x00 % x1) + x1) % x1;
}

bool solve() {
    int A, B, n; cin >> A >> B >> n;
    int r = n % B;
    if (0 == r) r = B;
    int x = calc(B, A, -r);
    // printf("x = %d\n", x);
    int n0 = r + x * B;
    return n <= n0;
}

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int T; cin >> T; while (T--) cout << (solve() ? "Yes\n" : "No\n");
    return 0;
}

H. Staircase Museum

我写 E 的时候队友把这题讨论出来了,但后面因为时间原因也没写完

赛后看了下队友的写法好像和官方题解不太一样,感觉很神奇

#include <bits/stdc++.h>
using namespace std;
const int INF = (int)1e9+5;

#define ft first
#define sd second

const int N = 2e6+50;
int l[N], r[N], yyy[4][N], dp[N], deg[N];
vector<int> ul, dr;
vector<int> G[N];
map<pair<int, int>, int> id; int cntid=0;
struct Node {
    int x, y, typ;
};
vector<Node> que; int ed=-1, fr=0;

// int allocate(pair<int, int> pr) {
//     if (!id.count(pr)) id[pr] = ++cntid;
//     return id[pr];
// }

int getdist() {
    vector<int> que2; int ed2=-1, fr2=0;
    for (int i=0; i<=cntid; ++i) {
        if (deg[i]==0) que2.push_back(i), ++ed2;
    }
    while (fr2 <= ed2) {
        int x = que2[fr2++];
        // printf("x=%d fr2=%d ed2=%d\n", x, fr2, ed2);
        for (int v : G[x]) if (deg[v]>0){
            // printf("v=%d\n", v);
            dp[v] = max(dp[v], dp[x]+1);
            --deg[v];
            if (0 == deg[v]) que2.push_back(v), ++ed2;
        }
    }
    return dp[0];
}

int solve() {
    int n; cin >> n;
    for (int i=1; i<=n; ++i) cin >> l[i] >> r[i];
    for (int i=0; i<=n; ++i) {
        if (i+1 <= n) yyy[0][i] = r[i+1]; else yyy[0][i] = INF;
        if (i > 0) yyy[1][i] = r[i]; else yyy[1][i] = r[1];
        if (i+1 <= n) yyy[2][i] = l[i+1]-1; else yyy[0][i] = INF;
        if (i > 0) yyy[3][i] = l[i]-1; else yyy[3][i] = l[1];
    }
    ul.push_back(-1);
    dr.push_back(-1);
    ul.push_back(0);
    for (int i=1; i<n; ++i) {
        if (yyy[0][i] != yyy[0][i-1]) ul.push_back(i);
    }
    for (int i=1; i<n; ++i) {
        if (yyy[3][i] != yyy[3][i+1]) dr.push_back(i);
    }
    dr.push_back(n);

    auto pr1 = make_pair(ul.back(), yyy[0][ul.back()]);
    // printf("ul:(%d %d)\n", ul.back(), yyy[0][ul.back()]);
    que.push_back(Node{pr1.ft, pr1.sd, 0}); ++ed;
    id[pr1] = ++cntid;
    dp[cntid] = 0;
    auto pr2 = make_pair(dr.back(), yyy[3][dr.back()]);
    // printf("dr:(%d %d)\n", dr.back(), yyy[3][dr.back()]);
    que.push_back(Node{pr2.ft, pr2.sd, 1}); ++ed;
    id[pr2] = ++cntid;
    dp[cntid] = 0;
    while (fr <= ed) {
        auto [x, y, typ] = que[fr++];
        int cur = id[make_pair(x, y)];
        // printf("x=%d y=%d typ=%d\n", x, y, typ); 
        if (0 == typ) {
            if (x > 0) {
                int x1 = x;
                int y1 = yyy[3][x];
                int tmpid;
                if (!id.count({x1, y1})) {
                    que.push_back(Node{x1, y1, 1}); ++ed;
                    id[{x1, y1}] = ++cntid;
                    tmpid = cntid;
                } else {
                    tmpid = id[{x1, y1}];
                }
                G[cur].push_back(tmpid);
                ++deg[tmpid];
            } else {
                G[cur].push_back(0);
                ++deg[0];
            }
            int x0 = *prev(lower_bound(ul.begin(), ul.end(), x));
            // printf("x0=%d\n", x0);
            if (x0 > -1) {
                int y0 = yyy[0][x0];
                int tmpid;
                if (!id.count({x0, y0})) {
                    que.push_back(Node{x0, y0, 0}); ++ed;
                    id[{x0, y0}] = ++cntid;
                    tmpid = cntid;
                } else {
                    tmpid = id[{x0, y0}];
                }
                G[cur].push_back(tmpid);
                ++deg[tmpid];
            }
        } else {
            if (y > l[1]-1) {
                int x0 = lower_bound(yyy[0], yyy[0]+n+1, y) - yyy[0];
                int y0 = y;
                int tmpid;
                if (!id.count({x0, y0})) {
                    que.push_back(Node{x0, y0, 0}); ++ed;
                    id[{x0, y0}] = ++cntid;
                    tmpid = cntid;
                } else {
                    tmpid = id[{x0, y0}];
                }
                G[cur].push_back(tmpid);
                ++deg[tmpid];
            } else {
                G[cur].push_back(0);
                ++deg[0];
            }
            int x1 = *prev(lower_bound(dr.begin(), dr.end(), x));
            if (x1 > -1) {
                int y1 = yyy[3][x1];
                int tmpid;
                if (!id.count({x1, y1})) {
                    que.push_back(Node{x1, y1, 1}); ++ed;
                    id[{x1, y1}] = ++cntid;
                    tmpid = cntid;
                } else {
                    tmpid = id[{x1, y1}];
                }
                G[cur].push_back(tmpid);
                ++deg[tmpid];
            }
        }
    }
    
    // for (int i=1; i<=cntid; ++i) {
    //     for (int v : G[i]) {
    //         dp[v] = max(dp[v], dp[i]+1);
    //     }
    // }
    int ans = getdist();

    // printf("y1:"); for (int i=0; i<=n; ++i) printf("%d ", yyy[1][i]); puts("");
    // for (auto [pri, idd] : id) {
    //     printf("(%d %d) %d\n", pri.ft, pri.sd, idd);
    // }
    // for (int i=1; i<=cntid; ++i) {
    //     printf("%d:", i);
    //     for (int v : G[i]) {
    //         printf("%d ", v);
    //     }puts("");
    // }
    // printf("dp:"); for (int i=0; i<=cntid; ++i) printf("%d ", dp[i]); puts("");

    for (int i=0; i<=cntid; ++i) {
        dp[i] = -1;
        deg[i] = 0;
        G[i].clear();
    }
    ul.clear(); dr.clear();
    id.clear(); cntid = 0;
    ed = -1, fr = 0; que.clear();

    return ans;
}

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int T; cin >> T; while (T--) cout << solve() << '\n';
    return 0;
}

I. Color-Balanced Tree

应该也是个签到构造,我题目都没看

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5+5;
int n, col[N], cnt[2];
int ed=-1, fr=0, que[N];
vector<int> G[N];

void solve() {
    cin >> n;
    ed = -1, fr = 0;
    cnt[0] = cnt[1] = 0;
    for (int i=1; i<=2*n; ++i) {
        col[i] = -1;
        G[i].clear();
    }
    for (int i=1; i<2*n; ++i) {
        int u, v; cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    col[1] = 1;
    ++cnt[1];
    que[++ed] = 1;
    while (fr<=ed) {
        int x = que[fr++];
        bool isleaf = true;
        for (int v : G[x]) if (-1 == col[v]) {
            col[v] = col[x]^1;
            ++cnt[col[v]];
            que[++ed] = v;
            isleaf = false;
        }
        if (isleaf) {
            --cnt[col[x]];
            col[x] = -1;
        }
    }
    // printf("n=%d\n", n);
    for (int i=1; i<=2*n; ++i) {
        if (-1==col[i]) {
            if (cnt[0] <= cnt[1]) {
                ++cnt[0];
                col[i] = 0;
            } else {
                ++cnt[1];
                col[i] = 1;
            }
        }
        cout << col[i] << (i==2*n ? '\n' : ' ');
    }
}

Postscript

五一训练保三争四吧,不然现在的水平和状态包去被薄纱的

posted @ 2025-05-02 14:23  空気力学の詩  阅读(148)  评论(0)    收藏  举报