The 3rd Universal Cup. Stage 18: Southeastern Europe

Preface

好经典的三个半小时 9 题下班啊

感觉复建了一段时间后写题的速度和准确度有所回暖,但不会做的题还是一点没招


A. All-Star

队友开局写的,我题目都没看

#include <bits/stdc++.h>

int n;
std::vector<int> out[1003];
int root;

void dfs(int cur, int pa) {
    for(auto out: out[cur]) if(out != pa) {
        std::cout << root << ' ' << cur << ' ' << out << char(10);
        dfs(out, cur);
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    
    std::cin >> n;
    for(int i = 1, u, v; i < n; ++i) {
        std::cin >> u >> v;
        out[u].emplace_back(v);
        out[v].emplace_back(u);
    }

    root = 1;
    for(int i = 2; i <= n; ++i) if(out[i].size() > out[root].size()) root = i;

    int ans = n - 1 - (int)out[root].size();
    std::cout << ans << char(10);

    for(auto out: out[root]) dfs(out, root);
    return 0;
}


C. Duloc Network

考虑对于两个不交点集 \(A,B\),若 \(query(A)+query(B)=query(A\cup B)\),则说明两个点集间距离大于 \(2\);反之则说明两个点集联通(距离小于等于 \(2\)

动态维护集合 \(S\) 表示当前已知的联通的点集,如果用上面方法判断 \(S\)\(V-S\) 不联通则已经得出答案,否则 \(V-S\) 中至少存在一个点与 \(S\) 联通

可以通过对 \(V-S\) 二分来找出对应的点,询问次数 \(2n\log n\le 3500\)

#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=205;
int n;
inline int query(vector <int> vec)
{
    static char s[N];
    for (RI i=1;i<=n;++i) s[i]='0';
    for (auto x:vec) s[x]='1';
    printf("? ");
    for (RI i=1;i<=n;++i) putchar(s[i]);
    printf("\n"); fflush(stdout);
    int d; scanf("%d",&d);
    return d;
}
inline void answer(CI x)
{
    printf("! %d\n",x); fflush(stdout);
}
int main()
{
    scanf("%d",&n);
    vector <int> IN={1},OUT;
    for (RI i=2;i<=n;++i) OUT.push_back(i);
    bool flag=1;
    while ((int)IN.size()<n)
    {
        int fin=query(IN);
        int l=0,r=(int)OUT.size()-1,res=-1;
        while (l<=r)
        {
            int mid=l+r>>1;
            vector <int> tmp;
            for (RI i=0;i<=mid;++i) tmp.push_back(OUT[i]);
            int fout=query(tmp);
            for (auto x:IN) tmp.push_back(x);
            int finout=query(tmp);
            if (fin+fout!=finout) res=mid,r=mid-1; else l=mid+1;
        }
        if (res==-1) { flag=0; break; }
        IN.push_back(OUT[res]);
        OUT.erase(OUT.begin()+res);
    }
    answer(flag);
    return 0;
}

D. Donkey and Puss in Boots

队友开局写的,我题都没看

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

const int N = 1e5+5;
int n, A[N];

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n;
    int sum = 0, mx = 0;
    for (int i=1; i<=n; ++i) cin >> A[i], sum += A[i], mx = max(mx, A[i]);
    if (0 == mx) cout << "Puss in Boots\n";
    else if (sum - mx >= n) cout << "Puss in Boots\n";
    else cout << "Donkey\n";
    return 0;
}

F. Magical Bags

首先对于初始局面下每对背包之间的 goodness 是很好判断的:把每个背包中的最小最大值看作一个区间,若对应的区间有交则说明这对背包之间是 good

因此把问题转化到区间上来,显然每个背包只会留下 \(\le 2\) 个物品,不难发现以下两个性质:

  • 若将某个区间删至孤点,设保留下的点权值为 \(x\),则 \(x\) 必须与所有本来和该区间相交的区间相交;
  • 若决定删除某个区间,则所有与该区间相交的区间都不能删除;

对于性质一,要判断一个区间是否能删,需要维护出所有与它相交的区间的交,这是个经典问题,将所有区间按左端点排序后用 set 维护前面,ST 表维护后面即可

对于性质二,我们考虑 DP 维护,令 \(f_i\) 表示处理了前 \(i\) 个区间的答案,不难发现转移是个 \((r_i,f_i)\) 的二维数点,用树状数组维护即可

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#define RI register int
#define CI const int&
using namespace std;
typedef pair <int,int> pi;
const int N=500005,INF=1e9;
int n,m,id[N],f[N]; pi itv[N]; vector <int> tv[N],v[N];
namespace ST
{
    int mnr[N][20],_lg[N];
    inline void init(void)
    {
        _lg[0]=-1;
        for (RI i=1;i<=n;++i) _lg[i]=_lg[i>>1]+1;
        for (RI i=1;i<=n;++i) mnr[i][0]=itv[i].second;
        for (RI j=1;(1<<j)<=n;++j)
        for (RI i=1;i+(1<<j)-1<=n;++i)
        mnr[i][j]=min(mnr[i][j-1],mnr[i+(1<<j-1)][j-1]);
    }
    inline int query(CI l,CI r)
    {
        int k=_lg[r-l+1];
        return min(mnr[l][k],mnr[r-(1<<k)+1][k]);
    }
}
class Tree_Array
{
    private:
        int bit[N];
    public:
        #define lowbit(x) (x&-x)
        inline void add(RI x,CI y)
        {
            for (;x<=m;x+=lowbit(x)) bit[x]=max(bit[x],y);
        }
        inline int get(RI x,int ret=0)
        {
            for (;x;x-=lowbit(x)) ret=max(ret,bit[x]); return ret;
        }
        #undef lowbit
}F;
int main()
{
    scanf("%d",&n); int sum=0;
    vector <int> rst;
    for (RI i=1;i<=n;++i)
    {
        int k; scanf("%d",&k); m+=k;
        tv[i].resize(k);
        for (RI j=0;j<k;++j)
        scanf("%d",&tv[i][j]),rst.push_back(tv[i][j]);
        sort(tv[i].begin(),tv[i].end());
        if (tv[i].size()==1) ++sum; else sum+=2;
    }
    sort(rst.begin(),rst.end());
    for (RI i=1;i<=n;++i)
    for (auto& x:tv[i]) x=lower_bound(rst.begin(),rst.end(),x)-rst.begin()+1;
    for (RI i=1;i<=n;++i) id[i]=i;
    auto cmp=[&](CI x,CI y)
    {
        return tv[x][0]<tv[y][0];
    };
    sort(id+1,id+n+1,cmp);
    for (RI i=1;i<=n;++i)
    v[i]=tv[id[i]],itv[i]={v[i][0],v[i].back()};
    // for (RI i=1;i<=n;++i) printf("[%d,%d]\n",v[i][0],v[i].back());
    ST::init(); set <int> Rpos; Rpos.insert(INF);
    for (RI i=1;i<=n;++i)
    {
        int l=itv[i].first,r=itv[i].second,L=0,R=INF;
        int pos=lower_bound(itv+i+1,itv+n+1,pi{r,r})-itv-1;
        if (i+1<=pos) L=itv[pos].first,R=ST::query(i+1,pos);
        R=min(R,*Rpos.upper_bound(l)); bool valid=0;
        if (L<=R)
        {
            auto p=lower_bound(v[i].begin(),v[i].end(),L);
            auto q=lower_bound(v[i].begin(),v[i].end(),R);
            if (p!=q) valid=1;
        }
        if (v[i].size()==1) valid=0;
        f[i]=max(f[i],f[i-1]);
        if (valid) f[i]=max(f[i],F.get(l)+1);
        Rpos.insert(r); F.add(r,f[i]);
    }
    return printf("%d",sum-f[n]),0;
}

G. Shrek's Song of the Swamp

队友开局写的,我题意都没看

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

const int N = 1e6+5;
int lst[N], dp[N], mx[N];

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int n; cin >> n;
    map<int, int> mp;
    for (int i=1; i<=n; ++i) {
        int a; cin >> a;
        lst[i] = mp[a];
        mp[a] = i;
    }
    for (int i=1; i<=n; ++i) {
        if (0 == lst[i]) dp[i] = 0;
        else {
            dp[i] = max(dp[lst[i]]+1, mx[lst[i]-1]+2);
        }
        mx[i] = max(mx[i-1], dp[i]);
    }
    int ans = 0;
    for (int i=1; i<=n; ++i) {
        ans = max(ans, dp[i]);
    }
    cout << ans << '\n';
    return 0;
}

H. Shreckless

刚开始犯病了写了两个假做法导致这场开题顺序和罚时都炸了

不难发现这题本质要求就是找出 \(n\) 对不相交的逆序对,考虑一列一列从后往前贪心

每次用 two pointers 尽可能多地匹配相邻两列的逆序对,多出的留到下次一定更优

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,m;  vector <int> col[N];
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        scanf("%d%d",&n,&m);
        for (RI j=1;j<=m;++j)
        col[j].resize(n+1),col[j][0]=1e9;
        for (RI i=1;i<=n;++i)
        for (RI j=1;j<=m;++j)
        scanf("%d",&col[j][i]);
        for (RI j=1;j<=m;++j) sort(col[j].begin(),col[j].end(),greater <int>());
        int cnt=0,lst=1;
        for (RI j=m;j>1;--j)
        {
            for (RI i=1;i<=n;++i)
            {
                while (lst<=n&&col[j][lst]>=col[j-1][i]) ++lst;
                if (lst<=n) ++cnt,++lst; else { lst=i; break; }
            }
        }
        puts(cnt>=n?"YES":"NO");
    }
    return 0;
}

J. Make Swamp Great Again

哈哈又是队友写的,回头发现这场感觉我一点没出力啊

#include <bits/stdc++.h>

int n;
int t[100000];

struct info_t {
    int count;
    bool hikari;
    info_t(): count(0), hikari(false) {}
};

std::map<int, info_t> ars;

inline int& get_t(int i) {
    if(i < 0) i += n;
    if(i >= n) i -= n;
    return t[i];
}
int min3(int a, int b, int c) { return std::min(a, std::min(b, c)); }
int max3(int a, int b, int c) { return std::max(a, std::max(b, c)); }

int main() {
    std::ios::sync_with_stdio(false);

    std::cin >> n;
    for(int i = 0; i < n; ++i) std::cin >> t[i];
    for(int i = 0; i < n; ++i) {
        ars[t[i]].count += 1;
        if(t[i] == min3(get_t(i - 2), get_t(i - 1), get_t(i))) ars[t[i]].hikari = true;
        if(t[i] == max3(get_t(i - 2), get_t(i - 1), get_t(i))) ars[t[i]].hikari = true;
        if(t[i] == min3(get_t(i - 1), get_t(i), get_t(i + 1))) ars[t[i]].hikari = true;
        if(t[i] == max3(get_t(i - 1), get_t(i), get_t(i + 1))) ars[t[i]].hikari = true;
        if(t[i] == min3(get_t(i), get_t(i + 1), get_t(i + 2))) ars[t[i]].hikari = true;
        if(t[i] == max3(get_t(i), get_t(i + 1), get_t(i + 2))) ars[t[i]].hikari = true;
    }   

    for(int i = 0; i < n; ++i) {
        auto [count, hikari] = ars[t[i]];
        std::cout << n - count + !hikari << char(i == n - 1 ? 10 : 32);
    }

    return 0;
}


K. Intrusive Donkey

考虑对于原序列第 \(i\) 个位置上的字符维护 \(a_i\) 表示其重复的次数,初始时所有 \(a_i=1\)

用线段树维护 \(\{a_i\}\),很容易发现不管是把修改的 \(l,r\) 映射到原字符,或者查询某个位置的字符都很容易用线段树上二分维护

而一次修改操作可以转化为 \(\{a_i\}\) 上的两次单点加与一次区间乘 \(2\),很容易直接维护

#include<cstdio>
#include<iostream>
#include<array>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int n,q; char s[N];
class Segment_Tree
{
    private:
        int sum[N<<2],tag[N<<2];
        inline void apply(CI now,CI mv)
        {
            sum[now]*=mv; tag[now]*=mv;
        }
        inline void pushup(CI now)
        {
            sum[now]=sum[now<<1]+sum[now<<1|1];
        }
        inline void pushdown(CI now)
        {
            if (tag[now]!=1) apply(now<<1,tag[now]),apply(now<<1|1,tag[now]),tag[now]=1;
        }
    public:
        #define TN CI now=1,CI l=1,CI r=n
        #define LS now<<1,l,mid
        #define RS now<<1|1,mid+1,r
        inline void build(TN)
        {
            sum[now]=r-l+1; tag[now]=1; if (l==r) return;
            int mid=l+r>>1; build(LS); build(RS);
        }
        inline void modify_add(CI pos,CI mv,TN)
        {
            if (l==r) return (void)(sum[now]+=mv);
            int mid=l+r>>1; pushdown(now);
            if (pos<=mid) modify_add(pos,mv,LS);
            else modify_add(pos,mv,RS);
            pushup(now);
        }
        inline void modify_mul(CI beg,CI end,TN)
        {
            if (beg<=l&&r<=end) return apply(now,2);
            int mid=l+r>>1; pushdown(now);
            if (beg<=mid) modify_mul(beg,end,LS);
            if (end>mid) modify_mul(beg,end,RS);
            pushup(now);
        }
        inline array <int,3> query(CI val,TN)
        {
            if (l==r) return {l,val,sum[now]};
            int mid=l+r>>1; pushdown(now);
            if (sum[now<<1]>=val) return query(val,LS);
            else return query(val-sum[now<<1],RS);
        }
        #undef TN
        #undef LS
        #undef RS
}SEG;
signed main()
{
    scanf("%lld%lld%s",&n,&q,s+1);
    for (SEG.build();q;--q)
    {
        int opt,l,r; scanf("%lld%lld",&opt,&l);
        if (opt==1)
        {
            scanf("%lld",&r);
            auto [p,a,vp]=SEG.query(l);
            auto [q,b,vq]=SEG.query(r);
            if (p==q) SEG.modify_add(p,b-a+1); else
            {
                SEG.modify_add(p,vp-a+1);
                SEG.modify_add(q,b);
                if (p+1<=q-1) SEG.modify_mul(p+1,q-1);
            }
        } else
        {
            auto [p,a,vp]=SEG.query(l);
            printf("%c\n",s[p]);
        }
    }
    return 0;
}

L. Ogre Sort

不难发现一次 \((x,y)\) 操作可以转化为 \(y\)\((x,1)\) 操作,因此不妨钦定所有操作的 \(y=1\)

简单手玩一下会发现此时只要从大到小考虑每个数,用树状数组维护位置即可

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

const int N = 3e5+5;

int n, A[N], pos[N];
struct Fenwick {
    int fen[N];
    int lb(int x) {return x&(-x);}
    void upd(int p, int v) {
        for (int i=p; i<=n; i+=lb(i)) fen[i] += v;
    }
    int qry(int p) {
        int res = 0;
        for (int i=p; i>0; i-=lb(i)) res += fen[i];
        return res;
    }
}fen;


signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n;
    for (int i=1; i<=n; ++i) cin >> A[i], pos[A[i]]=i;
    int xx = n;
    for (int i=n; i>0; --i) {
        fen.upd(i, 1);
        if (A[i]==xx) --xx;
    }
    int frt = 0;
    cout << xx << ' ' << xx << '\n';
    for (int i=xx; i>0; --i) {
        cout << fen.qry(pos[i]) + frt << ' ' << 1 << '\n';
        ++frt;
        fen.upd(pos[i], -1);
    }
    return 0;
}

Postscript

B 题经典找性质 counting 啥都看不出,直接倒闭

posted @ 2025-09-08 16:30  空気力学の詩  阅读(107)  评论(0)    收藏  举报