The 2022 ICPC Asia Xian Regional Contest(2022 西安 ABCDEFGJL)

Problem J. Strange Sum
题意:选最多两个数字(可以不选),使得和最大
思路:sort之后,一个不选,选最高,选最高和次高,这三个价值取最大值

code

const int N = 1e5 + 10;
int a[N];
int n;
 
void solve(){
    cin >> n;
    rep(i,1,n)  cin >> a[i];
 
    int ans = 0;
    sort(a + 1, a + 1 + n);
    cout << max({a[n], a[n] + a[n - 1], 0ll}) << endl;
}

Problem F. Hotel
题意:n个队伍,每个队伍3个字母表示性别,有两种房间(单人间和双人间),双人间的两人必须来自同一队伍的同一性别,问所有人入住的最小代价
思路:对于每一支队伍来说,看看有没有两个人性别相同,取 a n s + = m i n ( 3 ∗ c 1 , c 1 + c 2 ) ans += min(3 * c1, c1 + c2) ans+=min(3c1,c1+c2) 否则 a n s + = 3 ∗ c 1 ans += 3 * c1 ans+=3c1

code

void solve(){
    ll n, c1, c2;
    cin >> n >> c1 >> c2;
    c1 = min(c1, c2);
    ll ans = 0;
    for(int i = 1; i <= n; i ++){
        string s;
        cin >> s;
        sort(s.begin(), s.end());
        if(s[0] == s[1] || s[1] == s[2])
            ans += min(3 * c1, c1 + c2);
        else ans += 3 * c1;
    }
    cout << ans << endl;
}   

Problem C. Clone Ranran
题意:一开始有0个任务,可以花a分钟复制自己,可以花b分钟制作题目,问制作c个题目的最小耗时
思路:倍增求最小值就行
code

void solve(){
    ll a, b, c;
    cin >> a >> b >> c;
    ll ans = LNF;
    ll res = 0;
    for(ll i = 1; i <= 2 * c; i <<= 1){
        ans = min(ans, ((c + i - 1) / i) * b + res);
        res += a;
    }
    cout << ans << endl;
}   

Problem G. Perfect Word
题意:在给定字符串中找出最长的字符串,使得该字符串的所有非空子串都出现在给定字符串中
思路:对于一个合法字符串来说,一个字符也是子串,所以一个合法的字符串肯定是一堆在给定字符串中的字符拼接而成的,假设一个长度大于1的字串合法,那么两个长度大于1的字串拼接而成的字串肯定合法
由此可以推断出,一个字串合法,肯定是由两个小于当前字串的子串拼接而成,也就是说,一个字串合法,那么它第一个字符肯定出现在集合中,最后一个字符也出现在集合中,这样拼接出来的字串合法
所以check每一个长度非1的字串,看看是不是去头尾后时候合法就可以了

code

const int N = 1e5 + 10;
string s[N];
int n;
 
void solve(){
    cin >> n;
    for(int i = 1; i <= n; i ++)
        cin >> s[i];
 
    sort(s + 1, s + 1 + n, [](const string a, const string b){
        return a.size() < b.size();
    });
 
    set<string> st;
    int res = 0;
    for(int i = 1; i <= n; i ++){
        if(s[i].size() == 1 || (st.count(s[i].substr(0, s[i].size() - 1)) && st.count(s[i].substr(1)))){
            st.insert(s[i]);
            res = max(res, (int)s[i].size());
        }
    }
    cout << res << endl;
}   

Problem E. Find Maximum
题意:(自己翻译吧,也不长)
思路:
手搓几个数据发现,对于短区间中,假设最大值出现在 x x x,那么 x + 1 m o d    3 = 0 x+1\mod 3 = 0 x+1mod3=0,也就是说 x x x 处于边界上,这样我们可以枚举中点 i i i i i i 之前的保留,之后的就是边界值
然后我们发现,一个数字暴力dfs求值也就 l o g log log 级别的时间复杂度

code

ll f(ll x){
    if(!x) return 1ll;
    if(x % 3) return f(x - 1) + 1;
    else return f(x / 3) + 1;
}
 
void solve(){
    ll l, r;
    cin >> l >> r;
    vector<int> v;
    ll x = r;
    while(x){
        v.pb(x % 3);
        x /= 3;
    }
    reverse(v.begin(), v.end());
    ll ans = 0;
    for(int i = 0; i < v.size(); i ++){
        ll x = 0;
        for(int j = 0; j < i; j ++)
            x = 3 * x + v[j];
        x = 3 * x + v[i] - 1;
        for(int j = i + 1; j < v.size(); j ++)
            x = 3 * x + 2;
        if(x >= l)
            ans = max(ans, f(x));
    }
 
    cout << max({ans, f(l), f(r)}) << endl;
}   

Problem L. Tree
题意:求集合的最小个数,满足以下要求的一个即可
1、两点 u u u v v v,一个是另一个子树中的点
2、两点 u u u v v v,一个不是另一个的祖先节点

翻译以下就是,用最少的链和反链覆盖这颗树

思路:
拓扑排序,从叶子节点开始回溯
首先假设都是在一条链上的,那么答案就是叶子节点的个数,然后开始合并
和答案取min

code

const int N = 1e6 + 10;
int in[N];
int n;
int fa[N];
 
void solve(){
    cin >> n;
    for(int i = 2; i <= n; i ++){
        int x; cin >> x;
        fa[i] = x;
        in[i] ++, in[x] ++;
    }
 
    queue<int> q;
    for(int i = 2; i <= n; i ++){
        if(in[i] == 1) q.push(i);
        in[i] --;
    }
    int k, ans;
    k = 0;
    ans = n;
    while(q.size()){
        int sz = q.size();
        ans = min(ans, sz + k);
        k ++;
        while(sz --){
            int u = q.front(); q.pop();
            if(!fa[u]) continue;
            in[fa[u]] --;
            if(!in[fa[u]])
                q.push(fa[u]);
        }
    }
    cout << ans << endl;
    for(int i = 1; i <= n; i ++)
        fa[i] = in[i] = 0;
}  

Problem B. Cells Coloring
题意:*表示不能填数,.表示能填数,自己设定k,然后同一行同一列不存在一样的非0数字
思路:
建二分图,枚举k,每次从起点向每一行加一个流量为1的边,每一列向终点加一个流量为1的边
1、一个方法是,每次直接加边,然后跑一个网络流
2、另一个方法是在残留网络上加1的流量,跑网络流

如果板子足够快,方法1是能过的,方法2的时间复杂度更低
显然我的板子不够快,可以去榜单上cv一个够快的板子
下面是法2的code

const int V = 1010;
const int E = 101000;
template<typename T>
struct FlowGraph {
    int s, t, vtot;
    int head[V], etot;
    int dis[V], cur[V];
    struct edge {
        int v, nxt;
        T f;
    } e[E * 2];
    void addedge(int u,int v, T f){
        e[etot]= {v, head[u], f}; head[u] = etot++;
        e[etot]= {u, head[v], 0}; head[v] = etot++;
    }
 
    bool bfs() {
        for (int i = 1; i <= vtot; i++) {
            dis[i] = 0;
            cur[i] = head[i];
        }
        queue<int> q;
        q.push(s); dis[s] = 1;
        while (!q.empty()) {
            int u = q.front(); q.pop();
            for (int i = head[u]; ~i; i = e[i].nxt) {
                if (e[i].f && !dis[e[i].v]) {
                    int v = e[i].v;
                    dis[v] = dis[u] + 1;
                    if (v == t) return true;
                    q.push(v);
                }
            }
        }
        return false;
    }
 
    T dfs(int u, T m) {
        if (u == t) return m;
        T flow = 0;
        for (int i = cur[u]; ~i; cur[u] = i = e[i].nxt)
            if (e[i].f && dis[e[i].v] == dis[u] + 1) {
                T f = dfs(e[i].v, min(m, e[i].f));
                e[i].f -= f;
                e[i ^ 1].f += f;
                m -= f;
                flow += f;
                if (!m) break;
            }
        if (!flow) dis[u] = -1;
        return flow;
    }
    T dinic(){
        T flow=0;
        while (bfs()) flow += dfs(s, numeric_limits<T>::max());
        return flow;
    }
    void init(int s_, int t_, int vtot_) {
        s = s_;
        t = t_;
        vtot = vtot_;
        etot = 0;
        memset(head, -1, sizeof head);
    }
};
 
FlowGraph<ll> g;
const int N = 300;
char a[N][N];
int n, m, s, t;
ll c, d;
 
 
void solve(){
    cin >> n >> m >> c >> d;
 
    s = n + m + 1, t = n + m + 2;
    g.init(s, t, n + m + 2);
    ll sum = 0;
    for(int i = 1; i <= n; i ++)
        g.addedge(s, i, 0);
    for(int i = 1; i <= m; i ++)
        g.addedge(n + i, t, 0);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j ++){
            cin >> a[i][j];
            if(a[i][j] == '.'){
                sum ++;
                g.addedge(i, n + j, 1);
            }
        }
 
    ll ans = LNF;
    for(int k = 0; k <= max(n, m); k ++){
        ans = min(ans, c * k + d * sum);
        for(int i = 1; i <= n; i ++)
            g.e[i * 2 - 2].f ++;
        for(int j = 1; j <= m; j ++)
            g.e[(j + n) * 2 - 2].f ++;
        sum -= g.dinic();
    }
    cout << ans << endl;
}   

Problem A. Bridge
思路:分块处理
修改的话块内暴力
查询的话块间暴力跳
(我因为因为map没find,然后每次都mp[{x,i}]这样查询,导致mle,望周知)

code

const int N = 1e5 + 2, M = 320;
int a[M][N];
map<pair<int, int>, int> mp;
int n, m, q;
 
void solve(){
    // cout << sqrt(N) << endl;
    cin >> n >> m >> q;
    int sz = m / M + 1;
    for(int i = 1; i <= n; i ++)
        for(int j = 0; j <= sz; j ++)
            a[j][i] = i;
    while(q --){
        int op; cin >> op;
        if(op == 1){
            int u, v; cin >> u >> v;
            v --;
            int x = u, y = x + 1;
            mp[{u, v}] = y, mp[{u + 1, v}] = x;
            for(int i = v - 1; i >= (v / M) * M; i --){
                if(mp.find({x, i}) != mp.end()) x = mp[{x, i}];
                if(mp.find({y, i}) != mp.end()) y = mp[{y, i}];
            }
            int idx = v / M;
            swap(a[idx][x], a[idx][y]);
        }
        else{
            int u; cin >> u;
            for(int i = 0; i <= sz; i ++)
                u = a[i][u];
            cout << u << endl;
        }
    }
}   

Problem D. Contests
题意:(太困了,我先放一个code在这里,有缘再说)
思路:倍增

code

const int N = 1e5 + 10;
int n, m, q;
int a[10][N];
int p[6][N];
int f[21][6][N];
 
void solve(){
    cin >> n >> m;
    for(int i = 1; i <= m; i ++)
        for(int j = 1; j <= n; j ++){
            cin >> a[i][j];
            p[i][a[i][j]] = j;
        }
 
    for(int i = 1; i <= m; i ++){
        for(int j = 1; j <= n; j ++)
            f[0][i][j] = p[i][j];
        for(int j = 1; j <= m; j ++){
            int minn = INF;
            for(int k = n; k; k --){
                minn = min(minn, p[i][a[j][k]]);
                f[0][i][a[j][k]] = min(f[0][i][a[j][k]], minn);
            }
        }
    }
 
    for(int i = 1; i <= 20; i ++){
        for(int j = 1; j <= m; j ++){
            for(int k = 1; k <= n; k ++){
                f[i][j][k] = f[i - 1][j][k];
                for(int l = 1; l <= m; l ++){
                    f[i][j][k] = min(f[i][j][k], f[i - 1][j][a[l][f[i - 1][l][k]]]);
                }
            }
        }
    }
 
    cin >> q;
    while(q -- ){
        int x, y; cin >> x >> y;
        int ans = 0, st = 0;
        for(int i = 1; i <= m; i ++)
            if(p[i][x] < p[i][y]){
                st = 1;
                break;
            }
        if(st)
            cout << 1 << endl;
        else{
            vector<int> v(10);
            for(int i = 1; i <= m; i ++)
                v[i] = p[i][x];
            for(int i = 20; i >= 0; i --){
                vector<int> ne(10);
                st = 0;
                for(int j = 1; j <= m; j ++){
                    ne[j] = v[j];
                    for(int k = 1; k <= m; k ++){
                        ne[j] = min(ne[j], f[i][j][a[k][v[k]]]);
                        // if(ne[j] <= p[j][y]){
                        //     st = 1;
                        //     break;
                        // }
                    }
                    if(ne[j] <= p[j][y]){
                        st = 1;
                        break;
                    }
                }
                if(!st){
                    v = ne;
                    ans |= (1 << i);
                } 
            }
            // cout << ans << endl;
            if(ans == (1 << 21) - 1)
                cout << -1 << endl;
            else cout << ans + 2 << endl;
        }
    }
}   
  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值