22.8.11

22.8.11

CF1494D-Dogeforces

题意:

​ 给一个 \(n\), 代表公司中员工的数量, 然后公司有管理者, 存在多级管理模式(可以直接或间接管理员工), 每个管理者至少管理两个人, 保证管理者工资严格高于其所管理人的工资, 现在给一个 \(n*n\) 的数组 \(a\), \(a_{i,j}\) 的含义为第 \(i\) 个员工与第 \(j\) 个员工的公共管理者的工资, 当 \(i=j\) 时, 其含义为 第 \(i\) 个员工的工资, 现在公司有 \(k\) 个成员(未知), 要求我们复现出公司的管理结构

思路:

​ 可以考虑递归解决, 从根到叶子进行递归求解, 然后每个子树有一个代表这颗子树中叶子节点的编号的 \(vector\), 此时对于每颗子树, 我们去确定这颗数的根节点, 考虑 \(n^2\) 枚举, 我们找出此时叶子中 \(a_{i,j}\) 的最大值, 因为严格保证了管理者工资比被管理者工资高, 所以这个值一定是当前子树的根, 当子树叶子数目为 \(1\) 时, 不再递归下去, 此时该叶子就为子树的根节点, 于是, 解决了问题

int n;
cin>>n;
vector a(n+1,vector<int>(n+1));
vector<int> qwq(n);
for(int i=1;i<=n;i++) {
    for(int j=1;j<=n;j++) {
        cin>>a[i][j];
    }
    qwq[i-1]=i;
}
vector<int> g(n<<2),c(n<<2);
int cc=n;
function<void(vector<int>,int)> dfs=[&](vector<int> t,int fa)->void {
    if(int(t.size())==1) {
        g[t[0]]=fa;
        c[t[0]]=a[t[0]][t[0]];
        return;
    }
    int sz=t.size();
    int maxn=0;
    for(int i=0;i<sz;i++) {
        for(int j=i+1;j<sz;j++) {
            maxn=max(maxn,a[t[i]][t[j]]);
        }
    }
    c[++cc]=maxn;
    g[cc]=fa;
    int now=cc;
    vector<bool> vis(sz);
    for(int i=0;i<sz;i++) {
        if(vis[i]) continue;
        vector<int> l;
        l.emplace_back(t[i]);
        vis[i]=true;
        for(int j=i+1;j<sz;j++) {
            if(a[t[i]][t[j]]!=maxn) {
                vis[j]=true;
                l.emplace_back(t[j]);
            }
        }
        dfs(l,now);
    }
};
dfs(qwq,0);
cout<<cc<<endl;
for(int i=1;i<=cc;i++) {
    cout<<c[i]<<" \n"[i==cc];
}
cout<<n+1<<endl;
for(int i=1;i<=cc;i++) {
    if(g[i]!=0) cout<<i<<' '<<g[i]<<endl;
}

E-Bash游戏

思路:

考虑无论第一个人怎么拿, 第二个人一定能使此时二人拿的石子总数量为 \(k+1\), 于是不难得出结论

\(n\%(k+1)==0\) 时, 先手必输, 反之先手赢

int n,k;
cin>>n>>k;
if(n%(k+1)==0) {
    cout<<"B"<<endl;
} else {
    cout<<"A"<<endl;
}

G-Deleting Divisors

思路:

打表找规律

void solve() {
    int n;
    cin>>n;
    if(n&1) {
        cout<<"Bob\n";
    } else {
        if((n&-n)==n) {
            int k=__lg(n);
            if(k&1) {
                cout<<"Bob\n";
            } else {
                cout<<"Alice\n";
            }
        } else {
            cout<<"Alice\n";
        }
    }
}

F-小牛再战

思路:

考虑一个合理的状态, 两两成对相等, 当两两成对不相等时, 可以拿数量最多的堆调整为两两成对, 此时下一个人只能打破这种平衡状态, 一直维护到结束, 则维护者赢

其中拿数量最多的堆一定可以实现调整

这里仅证明 \(n\) 为偶数的时候, 若为奇数, 先手一定可以调整好, 所以先手必胜

不妨假设 \(a_1<=a_2<=a_3<=...<=a_n\)

此时需要调整为两两相等, 那么空余石子数为 \(a_n-a_1\)

需要的石子为 \(a_3-a_2+a_5-a_4+...+a_{n-1}-a_{n-2}<=a_3-a_1+a_5-a_3...+a_{n-1}-a_{n-3}\)

右边的式子化简得 \(a_{n-1}-a_1<=a_n-a_1\)

则一定可以调整好

所以结论为:

① 若为奇数个堆, 则先手必胜

② 若为偶数堆且两两成对堆石子数相等, 则先手必输, 反之先手必胜

string w="Win\n",l="Lose\n";
int n;
while(cin>>n,n) {
    vector<int> a(n+1);
    for(int i=1;i<=n;i++) {
        cin>>a[i];
    }
    sort(all(a));
    if(n&1) {
        cout<<w;
    } else {
        bool flag=true;
        for(int i=1;i<=n;i+=2) {
            if(a[i]==a[i+1]) {
                flag=false;
                break;
            }
        }
        cout<<(flag?w:l);
    }
}

B-Digital Deletions

思路:

特判以 0 开头的串为先手胜, 其余打表即可, 打表复杂度为 \(6*10*10^6=6e7\) 内, 可以轻松通过

//@Author: ZI_MA
#include<bits/stdc++.h>
using namespace std;
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define PP pair<int, int>
#define all(x) x.begin()+1,x.end()
#define endl '\n'
#define int long long
const int INF=1e16;
bool ans[1000050];
void init() {
    for(int i=1;i<1000000;i++) {
        int now=i,pw=1;
        while(now) {
            int t=now%10;
            now/=10;
            if(t==0) {
                ans[i]|=(!ans[now]);
            } else {
                for(int j=1;j<t+(now!=0);j++) {
                    ans[i]|=(!ans[i-j*pw]);
                }
            }
            pw*=10;
        }
    }
}
void solve() {
    string s;
    cin>>s;
    if(s[0]=='0') {
        cout<<"Yes"<<endl;
    } else {
        int tot=0;
        for(auto i:s) {
            tot=tot*10+i-'0';
        }
        cout<<(ans[tot]?"Yes":"No")<<endl;
    }
}
signed main() {
    IOS;
    init();
    int __=1;
    cin>>__;
    while(__--) solve();
    return 0;
}

H-栗酱的异或和

思路:

\(nim\) 博弈变形, 此时要判断的不止先手是否必胜, 还规定了先手第一轮一定要拿哪一堆石子, 此时判断先手是否必胜, 其实只要判断一下那一堆的石子数与所有堆石子数异或值之间的关系即可

即满足 \(a_k\geq a_k\oplus tot\) (其中 \(tot\) 为所有的异或和) 则先手必胜, 反之先手必输

int n,k;
cin>>n>>k;
vector<int> a(n+1);
int tot=0;
for(int i=1;i<=n;i++) {
    cin>>a[i];
    tot^=a[i];
}
if(!tot) {
    cout<<"No\n";
} else {
    if(a[k]>=(a[k]^tot)) cout<<"Yes\n";
    else cout<<"No\n";
}

A-Rake It In

思路:

暴搜即可, 分奇偶步, 奇数步取最大值, 偶数步取最小值

int a[5][5];
void solve() {
    int k;
    cin>>k;
    k*=2;
    for(int i=1;i<=4;i++) {
        for(int j=1;j<=4;j++) {
            cin>>a[i][j];
        }
    }
    function<int(int)> dfs=[&](int c)->int {
        if(c==k+1) return 0;
        int ret;
        if(c&1) ret=0;
        else ret=INF;
        for(int i=1;i<=3;i++) {
            for(int j=1;j<=3;j++) {
                swap(a[i][j],a[i][j+1]);
                swap(a[i+1][j],a[i+1][j+1]);
                swap(a[i+1][j],a[i][j+1]);
                if(c&1) ret=max(ret,a[i][j]+a[i][j+1]+a[i+1][j]+a[i+1][j+1]+dfs(c+1));
                else ret=min(ret,a[i][j]+a[i][j+1]+a[i+1][j]+a[i+1][j+1]+dfs(c+1));
                swap(a[i][j],a[i][j+1]);
                swap(a[i+1][j],a[i+1][j+1]);
                swap(a[i][j],a[i+1][j+1]);
            }
        }
        return ret;
    };
    cout<<dfs(1)<<endl;
}
posted @ 2022-08-11 19:33  ZI_MA  阅读(23)  评论(0)    收藏  举报