Moscow International Workshops 2017. Day 4. Lviv NU Contest, GP of Ukraine

Preface

国庆本以为空的一批结果忙的飞起,好不容易抽时间凑到三个人,结果被 Div2 小登们按在地上摩擦。


B. Card Game

签到,暴力枚举约数即可

#include<cstdio>
#include<iostream>
#include<map>
#define RI register int
#define CI const int&
using namespace std;
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    int mxn=0,mxc=0;
    map <int,int> cnt;
    for (RI i=1;i<=m;++i)
    {
        int x; scanf("%d",&x);
        mxn=max(mxn,x);
        mxc=max(mxc,++cnt[x]);
    }
    int ways=0;
    for (RI s=1;s*s<=n;++s)
    {
        auto check=[&](CI s)
        {
            if (s>=mxn&&n/s>=mxc) ++ways;
        };
        if (n%s!=0) continue;
        check(s);
        if (s*s!=n) check(n/s);
    }
    return puts(ways==1?"YES":"NO"),0;
}

C. The Most Expensive Gift

队友写的,好像是说根据观察最后可能的周期串长度不会很大,因此可以直接暴枚周期串然后跑序列自动机

#include <bits/stdc++.h>

int n;
std::string s;
int nxt[10000][3];

std::vector<std::pair<int, int>> try_move(
    const std::vector<std::pair<int, int>> &cur,
    int ch
) {
    std::vector<std::pair<int, int>> res;
    int p = 0, r = -1, l = cur.size();

    while(p < l) {
        // std::cerr << "FUCK: " << cur[p].second << ", " << ch << ", " << nxt[cur[p].second][ch] << char(10);
        int nx = nxt[cur[p].second][ch];
        if(nx >= n) break;
        res.emplace_back(cur[p].first, nx);
        do p += 1;
        while(p < l && cur[p].first <= nx);
    }

    return res;
}

int ans;

void dfs(
    const std::vector<std::pair<int, int>> &cur,
    int depth
) {
    if(cur.empty()) return ;
    ans = std::max(ans, (int)(depth * cur.size() * cur.size()));

    // std::cerr << "debug cur(" << (int)(depth * cur.size() * cur.size()) << ") = ";
    // for(auto [l, r]: cur) {
    //     std::cerr << " [" << l << ", " << r << "]";
    // }
    // std::cerr << "\n";
    
    if(depth >= 10 && (n / (depth + 1)) * (n / (depth + 1)) * (depth + 1) <= ans) return ;
    for(int ch = 0; ch < 3; ++ch) dfs(try_move(cur, ch), depth + 1);
}

int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin >> n;
    std::cin >> s;

    int ss[3] = {n, n, n};
    for(int i = n - 1; i >= 0; --i) {
        memcpy(nxt[i], ss, sizeof(ss));
        ss[s[i] - 'a'] = i;
    }

    int cnt[3] = {0, 0, 0};
    for(int i = 0; i < n; ++i) cnt[s[i] - 'a'] += 1;
    ans = *std::max_element(cnt, cnt + 3);
    ans = ans * ans;

    for(int ch = 0; ch < 3; ++ch) {
        std::vector<std::pair<int, int>> init;
        for(int i = ss[ch]; i < n; i = nxt[i][ch])
            init.emplace_back(i, i);
        dfs(init, 1);
    }

    std::cout << ans << char(10);
    return 0;
}

D. Cut the Cake

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

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

const int N = 205;

string str[N];
int n, m, k;
int col[N], row[N], S[N][N];
vector<int> ansr, ansc;

bool solve() {

    cin >> n >> m >> k;
    for (int i=1; i<=n; ++i) {
        cin >> str[i];
        str[i] = '$' + str[i];
        for (int j=1; j<=m; ++j) {
            int a = str[i][j]-'0';
            col[j] += a, row[i] += a;
            S[i][j] = S[i-1][j] + S[i][j-1] - S[i-1][j-1] + a;
        }
    }
    for (int j=1; j<=m; ++j) col[j] += col[j-1]; 
    for (int i=1; i<=n; ++i) row[i] += row[i-1];
    // printf("row:"); for (int i=1; i<=n; ++i) printf("%d ", row[i]); puts("");
    ansr.push_back(0); ansc.push_back(0);
    int lst = 0;
    for (int i=1; i<=n; ++i) {
        if (row[i] - row[lst] == k) {
            ansr.push_back(i);
            lst = i;
        }
    }
    lst = 0;
    for (int i=1; i<=m; ++i) {
        if (col[i] - col[lst] == k) {
            ansc.push_back(i);
            lst = i;
        }
    }
    
    // printf("r=%d c=%d\n", ansr.size(), ansc.size());
    if (ansr.size() != k+1 || ansc.size() != k+1) return false;
    // puts("11111");
    for (int i=1; i<=k; ++i) for (int j=1; j<=k; ++j) {
        int r=ansr[i], c=ansc[j], nr=ansr[i-1], nc=ansc[j-1];
        if (S[r][c]-S[nr][c]-S[r][nc]+S[nr][nc] != 1) return false;
    }
    return true;
}

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    bool ok = solve();
    if (!ok) cout << "NO\n";
    else {
        cout << "YES\n";
        for (int i=1; i<k; ++i) cout << ansr[i] << (i==k-1 ? '\n' : ' ');
        for (int i=1; i<k; ++i) cout << ansc[i] << (i==k-1 ? '\n' : ' ');
    }
    return 0;
}

F. Bad Word

首先如果串本身不是回文答案就是 \(1\)

可以证明,若串是 aaaaa,aabaa,ababa 等形式则一定无解,否则一定可以两次操作删空

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

int n;
string str;

bool all_same(string str) {
    int len = str.length();
    for (int i=1; i<len; ++i) if (str[i] != str[0]) return false;
    return true;
}

bool is_pan(string str) {
    int len = str.length();
    for (int i=0; i<len; ++i) if (str[i] != str[len-1-i]) return false;
    return true;
}

bool is_ab(string str) {
    int len = str.length();
    if (len <= 2) return false;
    for (int i=2; i<len; ++i) if (str[i] != str[i%2]) return false;
    return true;
}

int solve() {
    string str;
    cin >> n;
    cin >> str;
    if (!is_pan(str)) return 1;
    // cout << str << '\n';
    if (all_same(str)) return -1;
    if (n%2==0) return 2;
    if (is_ab(str)) return -1;
    if (all_same(str.substr(0, n/2)) && all_same(str.substr((n+1)/2, n/2))) return -1;
    return 2;    
}

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

G. Zenyk, Marichka and Interesting Game

首先根据经典的模仿策略,我们可以将每堆石子个数模 \(a+b\),同时 \(a=b\) 的情况很好处理

此时每堆石子都满足一个人取了后另一个人不能取,直观上考虑会堆数奇偶性有关

不妨假设 \(a<b\),此时发现如果存在一堆石子个数 \(x\) 满足 \(a\le x<b\)\(2a\le x\),则先手一定可以调整拿去次数的奇偶性从而必胜

对于 \(a>b\) 的情形,枚举先手拿哪一堆后就自动变为上述情况,简单判断即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,A,B,a[N];
int main()
{
    scanf("%d%d%d",&n,&A,&B);
    for (RI i=1;i<=n;++i)
    scanf("%d",&a[i]),a[i]%=(A+B);
    if (A==B)
    {
        int cnt=0;
        for (RI i=1;i<=n;++i)
        cnt+=a[i]/A;
        puts(cnt%2==1?"Zenyk":"Marichka");
        return 0;
    }
    int x=min(A,B),y=max(A,B),num[3]={0,0,0};
    for (RI i=1;i<=n;++i)
    {
        if (x<=a[i]&&a[i]<y) ++num[0];
        if (a[i]>=2*x) ++num[1];
        if (a[i]>=x) ++num[2];
    }
    if (A<B)
    {
        if (num[0]||num[1]) puts("Zenyk");
        else puts(num[2]%2==1?"Zenyk":"Marichka");
        return 0;
    }
    for (RI i=1;i<=n;++i)
    {
        if (a[i]<A) continue;
        if (x<=a[i]&&a[i]<y) --num[0];
        if (a[i]>=2*x) --num[1];
        if (a[i]>=x) --num[2];
        a[i]-=A;
        if (x<=a[i]&&a[i]<y) ++num[0];
        if (a[i]>=2*x) ++num[1];
        if (a[i]>=x) ++num[2];
        if (num[0]==0&&num[1]==0&&num[2]%2==0)
        {
            puts("Zenyk"); return 0;
        }
        if (x<=a[i]&&a[i]<y) --num[0];
        if (a[i]>=2*x) --num[1];
        if (a[i]>=x) --num[2];
        a[i]+=A;
        if (x<=a[i]&&a[i]<y) ++num[0];
        if (a[i]>=2*x) ++num[1];
        if (a[i]>=x) ++num[2];
    }
    return puts("Marichka"),0;
}

H. Frog Jumping

首先检查相邻石子间是否有间距大于 \(D\) 的,若有则让 \(c_i\) 最小的青蛙把石子全跳了,剩下的青蛙直接 \(1\to n\) 大跳

否则考虑二分有 \(s\) 只青蛙可以不进行大跳,检验的话就是把石子隔 \(s\) 个分个某个青蛙然后检查一遍距离即可

注意特判 \(1\to n\) 可以不消耗代价的情况

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<assert.h>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,m,k,d,c[N],a[N];
int main()
{
    scanf("%d%d%d%d",&n,&m,&k,&d);
    for (RI i=1;i<=m;++i) scanf("%d",&c[i]);
    sort(c+1,c+m+1);
    for (RI i=1;i<=k;++i) scanf("%d",&a[i]);
    sort(a+1,a+k+1); a[0]=1; a[k+1]=n;
    if (n-1<=d) return puts("0"),0;
    bool flag=0;
    for (RI i=0;i+1<=k+1;++i)
    if (a[i+1]-a[i]>d) { flag=1; break; }
    long long ans=0;
    if (flag)
    {
        for (RI i=0;i+1<=k+1;++i)
        if (a[i+1]-a[i]>d) ans+=c[1];
        for (RI i=2;i<=m;++i) ans+=c[i];
        return printf("%lld",ans),0;
    }
    int l=1,r=min(k,m),res=0;
    while (l<=r)
    {
        int mid=l+r>>1;
        auto check=[&](CI s)
        {
            for (RI i=1;i<=k;++i)
            {
                int pre=max(0,i-s);
                if (a[i]-a[pre]>d) return 0;
                int nxt=min(k+1,i+s);
                if (a[nxt]-a[i]>d) return 0;
            }
            return 1;
        };
        if (check(mid)) res=mid,l=mid+1; else r=mid-1;
    }
    assert(res!=0);
    for (RI i=1;i<=m-res;++i) ans+=c[i];
    return printf("%lld",ans),0;
}

I. Slot Machine

首先不难发现最优策略只会选择至多两种箱子进行取球

选一种箱子的情况根据鸽笼原理,求出其颜色数 \(col\) 后,若该箱子存在个数大于 \(1\) 的球,则用 \(col+1\) 更新答案

选两种箱子的话考虑对第一次取的箱子中每种颜色赋一个权值,表示抽出这种颜色后去其它箱子抽该颜色需要的最小次数

不难发现这个权值就是除了这个箱子外,有该颜色出现的箱子中的颜色数量(这里还是要用鸽笼原理分析一波)

求出权值后考虑最坏情况,即权值大的球总是被先抽出来,把权值降序排序更新答案即可

#include<cstdio>
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,INF=1e9;
int n,mn[N],smn[N],col[N]; map <int,int> box[N];
int main()
{
    int ans=INF;
    for (RI i=1;i<=100000;++i)
    mn[i]=smn[i]=INF;
    scanf("%d",&n);
    for (RI i=1;i<=n;++i)
    {
        int L; scanf("%d",&L);
        for (RI j=1;j<=L;++j)
        {
            int x; scanf("%d",&x);
            box[i][x]+=1;
        }
        col[i]=(int)box[i].size();
        for (auto [c,num]:box[i])
        {
            if (num>=2) ans=min(ans,col[i]+1);
            if (col[i]<mn[c]) smn[c]=mn[c],mn[c]=col[i];
            else if (col[i]<smn[c]) smn[c]=col[i];
        }
    }
    for (RI i=1;i<=n;++i)
    {
        vector <int> val;
        for (auto [c,num]:box[i])
        {
            int tmp=(mn[c]!=col[i]?mn[c]:smn[c]);
            val.push_back(tmp);
        }
        sort(val.begin(),val.end(),greater <int>());
        // printf("box %d: ",i);
        // for (auto x:val) printf("%d ",x); putchar('\n');
        for (RI i=0;i<(int)val.size();++i)
        ans=min(ans,val[i]+i+1);
    }
    return printf("%d",ans),0;
}

J. Half is Good

7 年后的评测机直接暴力秒了此题,如何呢

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

const int N = 1e7+5;
int n, m;
struct Edge{
    uint w; 
    int u, v;
}e[N];

int id[N],fa[N];
unsigned s;
int gf(int x) {return x==fa[x] ? x : fa[x]=gf(fa[x]);}
bitset <N> ans;

uint getNext() {
    s = (s ^ (s << 13));
    s = (s ^ (s >> 17));
    s = (s ^ (s << 5));
    return s;
}

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m >> s;
    for (int i = 0; i < m; ++i) {
        e[i].u = getNext() % n;
        e[i].v = getNext() % n;
        e[i].w = getNext() / 4;
        e[i].w = e[i].w * getNext(); // watch out for integer overflow
        id[i] = i;
        // there is an undirected edge between u and v with weight w
        // cout << e[i].u << ' ' << e[i].v << ' ' << e[i].w << '\n';
    }
    auto cmp = [&] (const int& x,const int& y) {
        return e[x].w < e[y].w;
    };
    sort(id, id+m, cmp);
    int cnt = 0;
    for (int i=0; i<n; ++i) fa[i] = i;
    for (int i=0; i<m; ++i) {
        int idx = id[i];
        if (gf(e[idx].u) != gf(e[idx].v)) {
            fa[gf(e[idx].u)] = gf(e[idx].v);
            ++cnt;
            ans[idx] = true;
            if (2*cnt>=n-1) break;
        }
    }

    uint res = 0;
    for (int i=0; i<m; ++i) {
        int j = i/32;
        if (j != (i-1)/32) {
            cout << res << ' ';
            res = 0;
        }
        if (ans[i]) res += (1LL << (i%32));
    }
    cout << res << '\n';

    return 0;
}

L. Impress Her

直接暴力,根据经典结论枚举每种数对应的矩形内部的所有点复杂度是 \(O(n^3)\) 的,打个时间戳即可快速统计

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=505,M=1e6+5,INF=1e9;
int n,m,a[N][N],vis[M],xl[M],xr[M],yl[M],yr[M];
int main()
{
    scanf("%d%d",&n,&m);
    for (RI i=1;i<=1000000;++i)
    xl[i]=yl[i]=INF,xr[i]=yr[i]=-INF;
    for (RI i=1;i<=n;++i)
    for (RI j=1;j<=m;++j)
    {
        int s; scanf("%d",&s); a[i][j]=s;
        xl[s]=min(xl[s],i); xr[s]=max(xr[s],i);
        yl[s]=min(yl[s],j); yr[s]=max(yr[s],j);
    }
    int ans=0;
    for (RI i=1;i<=1000000;++i)
    {
        if (xl[i]==INF) continue;
        for (RI x=xl[i];x<=xr[i];++x)
        for (RI y=yl[i];y<=yr[i];++y)
        {
            int j=a[x][y];
            if (j==i) continue;
            if (vis[j]==i) continue;
            if (xl[i]<=xl[j]&&xr[j]<=xr[i]&&yl[i]<=yl[j]&&yr[j]<=yr[i])
            {
                vis[j]=i; ++ans;
            }
        }
    }
    return printf("%d",ans),0;
}

Postscript

比赛临近还是一点不训啊,不会真要成旅游队了吧

posted @ 2025-10-06 17:56  空気力学の詩  阅读(4)  评论(0)    收藏  举报