首页 > 其他分享 >The 1st Universal Cup. Stage 19: America

The 1st Universal Cup. Stage 19: America

时间:2024-09-17 19:38:49浏览次数:1  
标签:now return Cup 19 Universal int && include id

Preface

中秋加训,发现 Ucup 里好多比赛要么之前做过,要么就是太毒瘤(有场比赛直接把模 \(998244353\) 写在题目名里了),因此就直接跳到这场北美区决赛了

这场前期因为卡题意以及卡签到打的还是挺难受的,不过好在恶心归恶心题目还是都能出,最后也是堪堪写了 9 题

由于这场没找到题解(其实找到了,但好像要FQ)剩下的题就先坑着了


A. Allergen Testing

刚开始想的是如果只有一天就是经典的二进制分组模型,天数较多的话就考虑平均分一下,交上去发现 WA 飞了

后面徐神出手发现其实就是把原来的二进制编码改成 \(d+1\) 进制编码,因此答案就是 \(\lceil\log_{d+1}n\rceil\)

for _ in range(int(input())):
    n, k = map(int, input().split())
    ans = 0
    k += 1
    i = 1
    while i < n:
        ans += 1
        i *= k
    print(ans)


B. A Tree and Two Edges

大力分讨题

考虑先求出一个生成树,并将多出的两条边记为 \((a,b),(c,d)\)

不难发现答案的上界就是 \(4\),要单纯的人为区分 \(1,2,3,4\) 的 Case 很复杂,但我们可以枚举所有可能的路径并检验其是否合法

以 \(u\to a\to b\to c\to d\to v\) 的路径来举例,其实只要看 \(u\sim a,b\sim c,d\sim v\) 这三部分路径上的点是否有重复

检验的话也很简单,用树剖把路径拆成若干个区间,判断这些区间是否有交即可,直接排序后判断即可

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=50005;
int n,q,x,y,fa[N],a,b,c,d; vector <int> v[N];
inline int getfa(CI x)
{
    return fa[x]!=x?fa[x]=getfa(fa[x]):x;
}
namespace Tree_Division
{
    int dep[N],dfn[N],sz[N],top[N],anc[N],son[N],idx;
    inline void DFS1(CI now=1,CI fa=0)
    {
        sz[now]=1; dep[now]=dep[fa]+1; anc[now]=fa;
        for (auto to:v[now]) if (to!=fa)
        {
            DFS1(to,now); sz[now]+=sz[to];
            if (sz[to]>sz[son[now]]) son[now]=to;
        }
    }
    inline void DFS2(CI now=1,CI topf=1)
    {
        dfn[now]=++idx; top[now]=topf;
        if (son[now]) DFS2(son[now],topf);
        for (auto to:v[now]) if (to!=anc[now]&&to!=son[now]) DFS2(to,to);
    }
    inline void modify(int x,int y,vector <pi>& Itv)
    {
        while (top[x]!=top[y])
        {
            if (dep[top[x]]<dep[top[y]]) swap(x,y);
            Itv.push_back({dfn[top[x]],dfn[x]});
            x=anc[top[x]];
        }
        if (dep[x]<dep[y]) swap(x,y);
        Itv.push_back({dfn[y],dfn[x]});
    }
};
using namespace Tree_Division;
inline bool check(vector <int> vec)
{
    vector <pi> Itv;
    for (RI i=0;i+1<vec.size();i+=2) modify(vec[i],vec[i+1],Itv);
    sort(Itv.begin(),Itv.end());
    for (RI i=0;i+1<Itv.size();++i)
    if (Itv[i].se>=Itv[i+1].fi) return 0;
    return 1;
}
int main()
{
    scanf("%d%d",&n,&q);
    for (RI i=1;i<=n;++i) fa[i]=i;
    for (RI i=1;i<=n+1;++i)
    {
        scanf("%d%d",&x,&y);
        if (getfa(x)==getfa(y))
        {
            if (a==0) a=x,b=y; else c=x,d=y;
            continue;
        }
        v[x].push_back(y); v[y].push_back(x);
        fa[getfa(x)]=getfa(y);
    }
    DFS1(); DFS2();
    for (RI i=1;i<=q;++i)
    {
        scanf("%d%d",&x,&y); int ans=1;
        ans+=check({x,a,b,y});
        ans+=check({x,b,a,y});
        ans+=check({x,c,d,y});
        ans+=check({x,d,c,y});
        ans+=check({x,a,b,c,d,y});
        ans+=check({x,a,b,d,c,y});
        ans+=check({x,b,a,c,d,y});
        ans+=check({x,b,a,d,c,y});
        ans+=check({x,c,d,a,b,y});
        ans+=check({x,c,d,b,a,y});
        ans+=check({x,d,c,a,b,y});
        ans+=check({x,d,c,b,a,y});
        printf("%d\n",ans);
    }
    return 0;
}

C. Broken Minimum Spanning Tree

考虑按照边权从小到大枚举所有的非树边 \((u,v,w)\),在树上遍历一遍找出 \(u\sim v\) 的路径上最长的边 \(w'\)

若 \(w<w'\) 则一定要发生替换,且由于我们枚举的顺序,当前替换了一定最优,所以直接模拟这个过程即可

#include<cstdio>
#include<iostream>
#include<set>
#include<vector>
#include<array>
#include<algorithm>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=3005;
int n,m,x[N],y[N],z[N],in[N],pre[N]; pi lst[N]; multiset <array <int,3>> v[N];
inline void addedge(CI id)
{
    v[x[id]].insert({y[id],z[id],id});
    v[y[id]].insert({x[id],z[id],id});
}
inline void deledge(CI id)
{
    v[x[id]].erase(v[x[id]].find({y[id],z[id],id}));
    v[y[id]].erase(v[y[id]].find({x[id],z[id],id}));
}
inline void find_path(CI now)
{
    for (auto [to,w,id]:v[now]) if (pre[to]==0)
    pre[to]=now,lst[to]={w,id},find_path(to);
}
int main()
{
    scanf("%d%d",&n,&m);
    vector <pi> E,ans;
    for (RI i=1;i<=m;++i)
    {
        scanf("%d%d%d",&x[i],&y[i],&z[i]);
        if (i<n) in[i]=1,addedge(i); else E.push_back({z[i],i});
    }
    sort(E.begin(),E.end());
    for (auto [_,id]:E)
    {
        if (in[id]) continue;
        for (RI i=1;i<=n;++i) pre[i]=0; pre[x[id]]=-1;
        find_path(x[id]); pi mx={0,0};
        for (int now=y[id];now!=x[id];now=pre[now]) mx=max(mx,lst[now]);
        if (mx.fi>z[id])
        {
            ans.push_back({id,mx.se});
            deledge(mx.se); in[mx.se]=0;
            addedge(id); in[id]=1;
        }
    }
    printf("%d\n",ans.size());
    for (auto [x,y]:ans) printf("%d %d\n",x,y);
    return 0;
}

F. Four Square

沟槽大力分讨题,大致思路就是先算出矩形的边长,然后考虑一定存在一个矩形 cover 了这个边长,递归处理即可

#include <bits/stdc++.h>

struct R { int h, w; };

using vii = std::vector<R>;

#define run(i) ({ std::cout << i << "\n"; return 0; })

bool solve(int h, int w, vii v) {
    // std::cerr << "solve(" << h << ", " << w << ")\n";
    if(h > w) std::swap(h, w);
    if(h < 0) return false;
    if(h == 0) return true;

    auto fres = [&v] (int i, int j) {
        vii res;
        for(int k = 0; k < v.size(); ++k) if(k != i && k != j) res.emplace_back(v[k]);
        return res;
    };
    
    for(int i = 0; i < v.size(); ++i) {
        if(v[i].w > w) return false;
    }

    for(int i = 0; i < v.size(); ++i) {
        if(v[i].w == w && solve(h - v[i].h, w, fres(i, -1))) return true;
        if(v[i].w == h && solve(w - v[i].h, h, fres(i, -1))) return true;
        if(v[i].h == w && solve(h - v[i].w, w, fres(i, -1))) return true;
        if(v[i].h == h && solve(w - v[i].w, h, fres(i, -1))) return true;
    }

    for(int i = 0; i < v.size(); ++i) for(int j = i + 1; j < v.size(); ++j) {
        if(v[i].w == v[j].w && v[i].h + v[j].h == w && solve(h - v[i].w, w, fres(i, j))) return true;
        if(v[i].h == v[j].h && v[i].w + v[j].w == w && solve(h - v[i].h, w, fres(i, j))) return true;
        if(v[i].w == v[j].h && v[i].h + v[j].w == w && solve(h - v[i].w, w, fres(i, j))) return true;
        if(v[i].h == v[j].w && v[i].w + v[j].h == w && solve(h - v[i].h, w, fres(i, j))) return true;
        if(v[i].w == v[j].w && v[i].h + v[j].h == h && solve(w - v[i].w, h, fres(i, j))) return true;
        if(v[i].h == v[j].h && v[i].w + v[j].w == h && solve(w - v[i].h, h, fres(i, j))) return true;
        if(v[i].w == v[j].h && v[i].h + v[j].w == h && solve(w - v[i].w, h, fres(i, j))) return true;
        if(v[i].h == v[j].w && v[i].w + v[j].h == h && solve(w - v[i].h, h, fres(i, j))) return true;
    }

    return false;
}

int main() {
    std::ios::sync_with_stdio(false);
    int ss = 0, t;
    vii rect(4, {0, 0});
    for(auto &[h, w]: rect) {
        std::cin >> h >> w;
        if(h > w) std::swap(h, w);
        ss += h * w;
    }
    t = std::sqrt(ss);
    if(t * t != ss) run(0);

    std::cout << solve(t, t, rect) << char(10);

    return 0;
}

G. Frequent Flier

小清新贪心题

不难发现我们可以用一个类似滑动窗口的东西维护当前区间内选了多少数,若这个值大于等于 \(k\) 则可以先贪心地不操作

否则考虑在当前区间内的某些位置选数,显然这个位置是越靠后越好;但因为某个位置可能填到了上界,因此不得不转而选择前面的位置

用经典 trick,拿一个并查集来维护从每个位置往前第一个还可以填数的位置,势能分析一下会发现复杂度很对

#include<cstdio>
#include<iostream>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=400005;
int n,m,k,f[N],g[N],fa[N];
inline int getfa(CI x)
{
    return fa[x]!=x?fa[x]=getfa(fa[x]):x;
}
signed main()
{
    scanf("%lld%lld%lld",&n,&m,&k);
    for (RI i=1;i<=n;++i) scanf("%lld",&f[i]);
    for (RI i=1;i<=n+m;++i) fa[i]=i;
    int sum=0,ans=0;
    for (RI i=1;i<=n+m-1;++i)
    {
        if (i-m>=1) sum-=g[i-m];
        if (sum>=k) continue;
        int x=getfa(i);
        while (x>0&&x>i-m&&sum<k)
        {
            int tmp=min(f[x]-g[x],k-sum);
            g[x]+=tmp; ans+=tmp; sum+=tmp;
            if (f[x]==g[x]) fa[x]=getfa(x-1),x=getfa(x);
        }
    }
    return printf("%lld",ans),0;
}

I. Power of Divisors

徐神开场写的签,我题意都不知道

from math import (pow, floor)

x = int(input())

if(x == 1):
    print(x)
    exit(0)

def f(n: int):
    count = 0
    i = 1
    while i * i < n:
        if n % i == 0:
            count += 2
        i += 1
    if i * i == n: count += 1
    return count

ans = 1_0000_0000_0000

for i in range(2, 64):
    dx = floor(pow(x, 1.0 / i))
    while dx ** i > x: dx -= 1
    while (dx + 1) ** i <= x: dx += 1
    if dx ** i != x: continue
    if f(dx) == i:
        ans = min(ans, dx)

print(-1 if ans == 1_0000_0000_0000 else ans)

J. Repetitive String Invention

很纯的 string 题,那当然是得让徐神 solo 了

#include <bits/stdc++.h>

constexpr int $n = 1605;

int go[$n][26], fail[$n], len[$n], las = 1, O = 1;
std::vector<int> occ[$n], ch[$n];

void insert(char c) {
    c -= 'a';
    int p = las, np = las = ++O;
    
    len[np] = len[p] + 1;
    memset(go[np], 0, sizeof(go[np]));
    for(; p && !go[p][c]; p = fail[p]) go[p][c] = np;

    if(!p) {
        fail[np] = 1;
        return ;
    }

    int q = go[p][c];
    if(len[q] == len[p] + 1) {
        fail[np] = q;
        return ;
    }

    int nq = ++O;
    memcpy(go[nq], go[q], sizeof(go[nq]));
    len[nq] = len[p] + 1;
    fail[nq] = fail[q];
    fail[np] = fail[q] = nq;
    for(; p && go[p][c] == q; p = fail[p]) go[p][c] = nq;

    return ;
}

bool vis[$n][$n];
int hkr[$n][$n];

int n;
void dfs(int cur) {
    for(auto ch: ch[cur]) {
        dfs(ch);
        for(int i = 1; i <= n; ++i) occ[cur][i] += occ[ch][i];
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    std::string s; std::cin >> s;
    n = s.size();
    for(int i = 0; i < n; ++i) insert(s[i]);
    for(int i = 1; i <= O; ++i) ch[fail[i]].emplace_back(i), occ[i].assign(n + 1, 0);
    for(int i = 1, cur = 1; i <= n; ++i) {
        cur = go[cur][s[i - 1] - 'a'];
        occ[cur][i] = 1; 
    }

    dfs(1);
    for(int i = 1; i <= O; ++i) for(int j = 1; j <= n; ++j) occ[i][j] += occ[i][j - 1];

    for(int l = 1; l <= n; ++l) for(int r = l, cur = 1; r <= n; ++r)
        hkr[l][r] = cur = go[cur][s[r - 1] - 'a'];

    int64_t ans = 0;

    for(int l = 1; l <= n; ++l) {
        for(int r = l, cur = 1; r <= n; ++r) {
            cur = go[cur][s[r - 1] - 'a'];
            assert(cur == hkr[l][r]); 
            // if(vis[cur][r - l + 1]) continue;
            // vis[cur][r - l + 1] = 1;
            // std::cerr << "l, r = " << l << ", " << r << char(10);
            for(int k = r + (r - l + 1); k <= n; ++k) if(occ[cur][k] - occ[cur][k - 1] == 1) {
                // std::cerr << "    k = " << k << char(10);
                ans += 1;
                if(k == r + (r - l + 1)) {
                    continue;
                }
                int cl = r + 1, cr = k - (r - l + 1), cc = hkr[cl][cr];
                // std::cerr << "        cl, cr = " << cl << ", " << cr << char(10);
                int prev = ans;
                ans += occ[cc][l - 1];
                if(k + (cr - cl) < n) ans += occ[cc][n] - occ[cc][k + (cr - cl + 1) - 1];
                // std::cerr << "        update = " << ans - prev << char(10);
            }
        }
    }

    std::cout << ans << char(10);

    return 0;
}

K. Space Alignment

读懂题目就很简单的一个题

首先很容易找出每一行是括号嵌套的第几层,并且可以求出每一行空格和 Tab 的数量,记作一个二元组

那么对于同层的所有二元组,若它们全部相同,则无法推出有用信息;否则其实以及可以唯一确定答案的值了,接下来要做的就是带回去检验一下

如果单独靠每层的信息无法推出答案,则考虑将相邻层的二元组作差,然后进行和上面类似的操作即可

#include<cstdio>
#include<iostream>
#include<vector>
#include<utility>
#include<cstring>
#include<algorithm>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
const int N=105;
typedef pair <int,int> pi;
int n,a[N],dep[N],stk[N],top,mxdep; pi st[N]; vector <pi> bkt[N]; char s[1005];
inline bool check(CI k)
{
    if (k<=0) return 0;
    static int res[N];
    for (RI i=0;i<=mxdep;++i)
    {
        vector <int> tmp;
        for (auto [x,y]:bkt[i]) tmp.push_back(x+y*k);
        sort(tmp.begin(),tmp.end());
        tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
        if (tmp.size()!=1) return 0;
        res[i]=tmp.back();
    }
    if (mxdep==0) return 1;
    vector <int> tmp;
    for (RI i=1;i<=mxdep;++i)
    tmp.push_back(res[i]-res[i-1]);
    sort(tmp.begin(),tmp.end());
    tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
    if (tmp.size()!=1) return 0;
    return tmp.back()>0;
}
int main()
{
    scanf("%d",&n);
    for (RI i=1;i<=n;++i)
    {
        scanf("%s",s+1);
        int len=strlen(s+1); pi cur={0,0};
        for (RI j=1;j<len;++j)
        if (s[j]=='s') ++cur.fi; else ++cur.se;
        st[i]=cur; a[i]=(s[len]=='{'?1:-1);
    }
    for (RI i=1;i<=n;++i)
    {
        if (a[i]==1) stk[++top]=i; else
        {
            int pre=stk[top--];
            dep[i]=dep[pre]=top;
            bkt[top].push_back(st[i]);
            bkt[top].push_back(st[pre]);
            mxdep=max(mxdep,top);
        }
    }
    for (RI i=0;i<=mxdep;++i)
    {
        sort(bkt[i].begin(),bkt[i].end());
        bkt[i].erase(unique(bkt[i].begin(),bkt[i].end()),bkt[i].end());
        if (bkt[i].size()==1) continue;
        auto [a,b]=bkt[i][0]; auto [c,d]=bkt[i][1];
        if ((a==c&&b!=d)||(a!=c&&b==d)) return puts("-1"),0;
        if (abs(c-a)%abs(b-d)!=0) return puts("-1"),0;
        int k=(c-a)/(b-d);
        if (check(k)) return printf("%d",k),0; else return puts("-1"),0;
    }
    if (mxdep==0) return puts("1"),0;
    vector <pi> tmp;
    for (RI i=1;i<=mxdep;++i)
    {
        auto [a,b]=bkt[i-1].back(); auto [c,d]=bkt[i].back();
        tmp.push_back({c-a,d-b});
    }
    sort(tmp.begin(),tmp.end());
    tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
    if (tmp.size()==1)
    {
        if (check(1)) return puts("1"),0; else return puts("-1"),0;
    }
    auto [a,b]=tmp[0]; auto [c,d]=tmp[1];
    if ((a==c&&b!=d)||(a!=c&&b==d)) return puts("-1"),0;
    if (abs(c-a)%abs(b-d)!=0) return puts("-1"),0;
    int k=(c-a)/(b-d);
    if (check(k)) return printf("%d",k),0; else return puts("-1"),0;
    return 0;
}

L. Splitting Pairs

遇到博弈直接打表启动,打了表后也是不难找到规律:

  • 当 \(n\) 为偶数时,当且仅当所有 \(a_i\) 均为奇数时,先手必败;
  • 当 \(n\) 为奇数时,当且仅当所有 \(\operatorname{lowbit}(a_i)\) 全相同时,先手必败;

具体证明可以用经典的镜像法则推导

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

int lb(int x) {return x&(-x);}

int solve() {
    int n; cin >> n;
    vector<int> vec(n);
    for (int i=0; i<n; ++i) cin >> vec[i];
    
    if (n%2==0) {
        for (int i=0; i<n; ++i) if (vec[i]%2==0) return 1;
        return 0;
    }

    for (int i=1; i<n; ++i) {
        if (lb(vec[0]) != lb(vec[i])) return 1;
    }
    return 0;
}

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

Postscript

美好的中秋假期就在一天网络赛,两天训练中结束了,真是团团又圆圆啊

标签:now,return,Cup,19,Universal,int,&&,include,id
From: https://www.cnblogs.com/cjjsb/p/18417389

相关文章

  • 代码随想录算法训练营第四天|24两两交换链表中的节点 19删除链表的倒数第N个节点 02.0
    24.两两交换链表中的节点给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。由于今天赶时间,第一眼看完题目没思路就直接看文字解析了,但还是复述一下看完之后自己的理解/想法/思路。这一题感觉对......
  • Leetcode 19.删除链表的倒数第第N个结点
    1.题目基本信息题目:给你一个链表,删除链表的倒数第n个结点,并且返回链表的头结点。地址:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/2.解题方法2.1.解题思路使用快慢指针2.2.解题步骤第一步,初始化快指针为head,慢指针指向一个哑结点,哑结点......
  • 【数据分享】1975-2020年人口密度POP栅格数据
    数据来源GHS-POP空间栅格产品(GHS-POP_GLOBE_R2023)描绘了人口的分布,表示为每个单元的人数。这个数据集描述了人口的分布和密度,以每个单元的人数表示,1975年-2020年,五年间隔,空间分辨率1000m.官网下载地址:https://human-settlement.emergency.copernicus.eu/download.php?ds=po......
  • 【数据分享】1985-2023年30米CLCD土地覆盖栅格数据分享
    1.数据来源​CLCD反映了中国快速的城市化进程和一系列生态工程,揭示了气候变化条件下人为对LC的影响,其在全球变化研究中具有潜在应用价值。2021年7月,武汉大学杨杰、黄昕两位教授共同撰写题为《30mannuallandcoveranditsdynamicsinChinafrom1990to2019》的研究论......
  • 201909-2 小明种苹果(续)ccfcsp
    一道简单的模拟。。。includeincludeusingnamespacestd;intmain(){constintN=1010;booldrop[N]={false};intn,m,i,j,cnt=0,cnt1=0;cin>>n;inty;intsum=0,sum1,temp=0;intindex;for(i=0;i<n;i++){ sum1=0;scanf("%d",&m);for(j=0;j&......
  • 历年CSP-J初赛真题解析 | 2019年CSP-J初赛阅读程序(16-33)
    学习C++从娃娃抓起!记录下CSP-J备考学习过程中的题目,记录每一个瞬间。附上汇总贴:历年CSP-J初赛真题解析|汇总_热爱编程的通信人的博客-CSDN博客#include<cstdio>#include<cstring>usingnamespacestd;charst[100];intmain(){scanf("%s",st);intn......
  • The 1st Universal Cup. Stage 12: Ōokayama
    Preface久违地训练,因为昨天ICPC网络赛打的太唐不得不上点强度了回到这场本身,由于中途发现了两个题被搬到去年暑假前集训队内赛了,导致经典提前没事干2h15min过了六个题后(有两个是原)就开始对着L,M发呆,虽然最后4h45min的时候把M开出来了,但还是说明做难题的水平不够(评价是......
  • leetcode刷题day19|二叉树Part07(235. 二叉搜索树的最近公共祖先、701.二叉搜索树中的
    235.二叉搜索树的最近公共祖先思路:二叉搜索树首先考虑中序遍历。根据二叉搜索树的特性,如果p,q分别在中间节点的左右两边,该中间节点一定是最近公共祖先,如果在同一侧,则递归这一侧即可。递归三部曲:1、传入参数:根节点,p,q,返回节点。2、终止条件:因为p,q一定存在,所以不会遍历到......
  • 【洛谷 P1216】[USACO1.5] [IOI1994]数字三角形 Number Triangles 题解(动态规划)
    [USACO1.5][IOI1994]数字三角形NumberTriangles题目描述观察下面的数字金字塔。写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。在上面的样例中,从的路径产生了最大权值。输入格式第一个行一个正整数......
  • [GXYCTF2019]BabyUpload 1
    打开靶机,上传文件抓包后缀不能带ph,大小写也无法绕过,意味着phtml后缀也无法上传对后缀只过滤ph,我们转变思路上传图片马,用.htaccess使图片马以php格式打开上传图片马上传失败,试一试过滤了哪些字符文件内容过滤了<?我们尝试另一种写法后成功上传<scriptlanguage="php">eval......