二次元音游人

这是一股照亮混沌的令和时代互联网的一道光,给在电子的海洋里冲浪的阿宅们带来笑容

Codeforces 2208 & 2204

【初音ミク】背景,沉溺於夏天【ナブナ】

廃颓 十九の伞に灯がついてる 今日も
在颓废十九岁的伞下灯光亮起 今天也是如此

Codeforces Round 1086 (Div. 2)

答题情况

image

\(C\) 打的不太好,然后 \(D\) 也没想到

A. Bingo Candies 473

image

**统计一下每种颜色的数量,如果最大值大于 \(n(n-1)\) (即 \(n^2\) 去掉对角线的 \(n\) 个)那就是 No **

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=105;
int a[114514];
inline void solve(){
    cin>>n;
    int maxn=0;
    for(int i=1;i<=n*n;i++){
        a[i]=0;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int x;
            cin>>x;
            a[x]++;
            maxn=max(maxn,a[x]);
        }
    }
    if(maxn<=n*(n-1)){
        cout<<"YES\n";
    }
    else{
        cout<<"NO\n";
    }
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

B. Cyclists 996

image

直接模拟,前 \(k\) 个看成一个小根堆用优先队列实现,后面的看成一个队列,然后着重看胜利条件牌。刚开始如果胜利条件牌在前 \(k\) 个牌里,那就选上它,模拟一下再把它放到队列末尾。初始化优先队列和队列之后循环遍历:一开始先从优先队列里打出最小的牌,放到队列末尾。然后从队列抽牌,如果队列第一个出来是胜利条件牌,就模拟一下再把它放回队列末尾(这时要注意前面队列少了一张牌,所以再从后面队列抽一张)。如果队列第一个出来不是胜利条件牌,就放到前面队列即可。

总体难度不难,小模拟题,多调几下就能出来

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,p,m;
const int N=5005;
int a[N];
inline void solve(){
    cin>>n>>k>>p>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    if(n==k){
        cout<<(m/a[p])<<'\n';
        return;
    }
    queue<pair<int,int>> behind;
    priority_queue<int,vector<int>,greater<int> > q;
    
    int tot=0;
    bool flag=false;
    int ans=0;
    if(p>k){
        flag=false;
        for(int i=1;i<=k;i++){
            q.push(a[i]);
        }
        for(int i=k+1;i<=n;i++){
            if(i==p){
                behind.push({a[i],1});
            }
            else{
                behind.push({a[i],0});
            }
        }
    }
    else{
        flag=true;
        tot+=a[p];
        ans=1;
        for(int i=1;i<=k+1;i++){
            if(i==p) continue;
            q.push(a[i]);
        }
        for(int i=k+2;i<=n;i++){
            behind.push({a[i],0});
        }
        behind.push({a[p],1});
    }
    while(1){
        // int tmp=0;
        int x=q.top();
        q.pop();
        // tmp+=x;
        tot+=x;
        if(tot>m) break;
        auto y=behind.front();
        behind.pop();
        if(y.second==1){
            behind.push({x,0});
            tot+=y.first;
            if(tot>m) break;
            ans++;
            behind.push({a[p],1});
            auto z=behind.front();
            behind.pop();
            q.push(z.first);
        }
        else{
            behind.push({x,0});
            q.push(y.first);
        }
        // if(tot+tmp>m) break;
    }
    cout<<ans<<'\n';
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
/*
1
8 4 7 10
3 4 4 2 1 1 4 2

3 4 4 1 1 4 2 2 //2
3 4 4 1 4 2 2 1 //3
3 4 4 4 2 2 1 1 //4

*/

C. Stamina and Tasks 1243

image

image

\(n\) 个任务,只能顺序选择选或者不选。选的话会降 \(S\)

假设从某地方 \(i\) 开始算后面的价值,相当于 \(S\times Value\),这里 \(S\) 其实就是一个系数,不影响后面。

\(dp[i]\) 为从 \(i\) 开始算价值(\(S=1\))的最大值,那么从后往前推可以得到转移方程

\[dp[i] = \max(dp[i+1], c[i]+dp[i+1] \times (1 - \frac{p_i}{100})) \]

然后这个 \(dp[i]\) 可以滚动掉

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=1e5+5;
int a[N];
int c[N],p[N];
inline void solve(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>c[i]>>p[i];
    }
    double ans=0.0;
    for(int i=n;i>=1;i--){
        ans=max(ans,c[i]+(1.0-1.0*p[i]/100)*ans);
    }
    cout<<fixed<<setprecision(8)<<ans<<'\n';
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

D1. Tree Orientation (Easy Version) 1724

image
image
image

题目大意是给你指定每个节点顺着有向树可以到达的节点,让你给这棵树的边标明方向。

简单版本下 \(n\le 500\) ,所以可以用 \(n^3\) 方法

枚举节点 \(u\) 和节点 \(v\) ,再枚举中间节点 \(k\) ,如果 \(u\) 可以到达 \(v\),并且没有 \(k\) 连通 \(u\)\(v\) ,就说明连边 \(u\rightarrow v\) 是树上的有向边。

找出来所有有向边之后,如果

  • 边的数量不是 \(n-1\)
  • 检查连通性( \(bfs\)\(dfs\),并查集)之后不连通
  • 把边重新连成树后检查一下连通图表和给出的不一样

就输出 \(no\),否则输出 \(yes\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=1005;
vector<pair<int,int>> edges;
inline bool liantong(){
    vector<vector<int>> E(n);
    for(auto [u,v]:edges){
        E[u].push_back(v);
        E[v].push_back(u);
    }
    
    vector<bool> vis(n,false);
    queue<int> q;
    q.push(0);//0为根
    vis[0]=true;
    int cnt=1;
    while(!q.empty()){
        int now=q.front();
        q.pop();
        for(int v:E[now]){
            if(!vis[v]){
                vis[v]=true;
                cnt++;
                q.push(v);
            }
        }
    }
    return cnt==n;
}
bool reach[N][N];
inline void solve(){
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            reach[i][j]=false;
        }
    }
    vector<string> s(n);
    bool FFF=false;
    for(int i=0;i<n;i++){
        cin>>s[i];
        if(s[i][i]!='1'){
            FFF=true;
        }
    }
    if(FFF){
        cout<<"No\n";
        return;
    }
    edges.clear();
    for(int u=0;u<n;u++){
        for(int v=0;v<n;v++){
            if(u==v) continue;
            if(s[u][v]=='0') continue;
            bool flag=false;
            for(int k=0;k<n;k++){
                if(k==u||k==v) continue;
                if(s[u][k]=='1'&&s[k][v]=='1'){
                    flag=true;
                    break;
                }
            }
            if(!flag){
                edges.push_back({u,v});
            }
        }
    }
    if(edges.size()!=n-1){
        cout<<"No\n";
        return;
    }
    if(!liantong()){
        cout<<"No\n";
        return;
    }
    vector<vector<int>> E(n);
    for(auto [u,v]:edges){
        E[u].push_back(v);
    }
    for(int u=0;u<n;u++){
        queue<int> q;
        q.push(u);
        reach[u][u]=true;
        while(!q.empty()){
            int now=q.front();
            q.pop();
            for(int v:E[now]){
                if(!reach[u][v]){
                    reach[u][v]=true;
                    q.push(v);
                }
            }
        }
    }
    bool flag=true;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if((reach[i][j]==1&&s[i][j]=='0')||(reach[i][j]==0&&s[i][j]=='1')){
                flag=false;
                break;
            }
        }
        if(!flag){
            cout<<"No\n";
            return;
        }
    }
    cout<<"Yes\n";
    for(auto [u,v]:edges){
        cout<<u+1<<" "<<v+1<<'\n';
    }
    return;

}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

D2. Tree Orientation (Hard Version) 2148

数据范围换成 \(n\le 8000\)

得换成 \(n^2\) 方法了

统计每个点可以到达几个点记作 \(cnt_i\),并降序排列。这样的话 \(cnt_i\) 越大的节点 \(i\),就越是这颗树的核心部分。从大到小排序可以有一个好处,如果一个节点指向另外一堆节点,那么这个根节点的 \(cnt_i\) 肯定比子节点的大,进而排除子节点的干扰。

\(1\)\(n\) 遍历 \(u\),然后枚举 \(cnt_i\) 大的节点 \(v\),如果当前遍历节点 \(u\) 可以到达 \(v\),说明边 \(u\rightarrow v\) 是树上一条有向边(反证法:如果不是的话,那肯定中间有节点可以链接 \(u\)\(v\),那这个节点肯定 \(cnt_i\) 更大,之前就遍历过了),再把 \(v\) 的可连通的节点 \(w\) 临时删掉(vis[w]=true)。如此循环到所有边都添加完毕。

之后判断 noyes 的方法和上一题一样。

上一题使用 \(bfs\) 判断连通性,本题用并查集

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=10005;
struct Dsu{
    int fa[N];
    inline void init(){
        for(int i=1;i<=n;i++){
            fa[i]=i;
        }
    }
    inline int find(int x){
        return (x==fa[x])?x:find(fa[x]);
    }
    inline void merge(int x,int y){
        fa[find(x)]=find(y);
    }
}dsu;
bool reach[N];
vector<int> tree[N];
void search(int u){
    reach[u]=1;
    for(int v:tree[u]){
        search(v);
    }
}

int id[N],cnt[N];
bool vis[N],e[N][N];
inline void solve(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cnt[i]=0;
        tree[i].clear();
    }
    for(int i=1;i<=n;i++){
        id[i]=i;
        string s;
        cin>>s;
        for(int j=1;j<=n;j++){
            e[i][j]=s[j-1]-'0';
            cnt[i]+=e[i][j];
        }
    }
    sort(id+1,id+n+1,[&](int u,int v)->bool{return cnt[u]>cnt[v];});
    vector<pair<int,int>> edges;
    for(int u=1;u<=n;u++){
        for(int i=1;i<=n;i++){
            vis[i]=false;
        }
        vis[u]=true;
        for(int i=1;i<=n;i++){
            int v=id[i];
            if(!vis[v]&&e[u][v]){
                edges.push_back({u,v});
                if(edges.size()>=n){
                    cout<<"No\n";
                    return;
                }
                for(int w=1;w<=n;w++){
                    if(e[v][w]){
                        vis[w]=true;
                    }
                }
            }
        }
    }
    if(edges.size()!=n-1){
        cout<<"No\n";
        return;
    }
    sort(edges.begin(),edges.end());
    dsu.init();
    for(auto [u,v]:edges){
        dsu.merge(u,v);
        tree[u].push_back(v);
    }
    int flag=true;
    for(int i=1;i<=n;i++){
        flag&=(dsu.find(i)==dsu.find(1));
    }
    if(!flag){
        cout<<"No\n";
        return;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            reach[j]=0;
        }
        search(i);
        for(int j=1;j<=n;j++){
            if(reach[j]!=e[i][j]){
                cout<<"No\n";
                return;
            }
        }
    }
    cout<<"Yes\n";
    for(auto [u,v]:edges){
        cout<<u<<" "<<v<<'\n';
    }
    return;

}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

E. Counting Cute Arrays 2619

暂无

Educational Codeforces Round 188 (Rated for Div. 2)

image

A. Passing the Ball 270

image

纯签到题,发现 \(n\) 范围很小,直接从头开始暴力模拟即可,复杂度 \(O(n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=105;
int a[114514];
inline void solve(){
    cin>>n;
    int maxn=0;
    for(int i=1;i<=n*n;i++){
        a[i]=0;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int x;
            cin>>x;
            a[x]++;
            maxn=max(maxn,a[x]);
        }
    }
    if(maxn<=n*(n-1)){
        cout<<"YES\n";
    }
    else{
        cout<<"NO\n";
    }
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

B. Right Maximum 652

image

举例 1 3 2 3 1,先选中 '3 1',再选中 '3 2',最后选中 '1'

顺序遍历,记录最大值,如果当前值大于等于记录的最大值,那么答案加一

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=105;
int a[114514];
inline void solve(){
    cin>>n;
    int now=0,ans=0;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        if(x>=now){
            ans++;
            now=x;
        }
    }
    cout<<ans<<'\n';
}
//1 3 3 1
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

C. Spring 811

image

简单的容斥原理,算出 \(A\),\(B\),\(C\) 单独的概率和 \(AB\),\(AC\),\(BC\) 单独的概率,和 \(ABC\) 的概率即可

详见代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b,c,m;
inline int LCM(int a,int b){
    return a*b/__gcd(a,b);
}
inline int S(int x){
    return m/x;
}
inline void solve(){
    cin>>a>>b>>c>>m; 
    int A=S(a)-S(LCM(a,b))-S(LCM(a,c))+S(LCM(LCM(a,b),c));
    int B=S(b)-S(LCM(a,b))-S(LCM(b,c))+S(LCM(LCM(a,b),c));
    int C=S(c)-S(LCM(b,c))-S(LCM(a,c))+S(LCM(LCM(a,b),c));
    int AB=S(LCM(a,b))-S(LCM(LCM(a,b),c));
    int BC=S(LCM(b,c))-S(LCM(LCM(a,b),c));
    int AC=S(LCM(a,c))-S(LCM(LCM(a,b),c));
    int ABC=S(LCM(LCM(a,b),c));
    int ansA=6*A+3*AB+3*AC+2*ABC;
    int ansB=6*B+3*AB+3*BC+2*ABC;
    int ansC=6*C+3*BC+3*AC+2*ABC;
    cout<<ansA<<" "<<ansB<<" "<<ansC<<'\n';
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

D. Alternating Path 1361

image

这题题意还是比较绕的,就是说给一个无向图规定方向,如果某节点开始所有路径经过边是正逆正逆循环的就叫完美节点,问最多多少完美节点

这里附上一张二分图示例

image

只有 \(x\rightarrow y\)\(y \rightarrow x\),没有 \(x \rightarrow x\)\(y \rightarrow y\)

image

其实就是给图奇偶分层,分成 \(A\) 集合和 \(B\) 集合,然后因为第一条边必须是正向,所以贡献是 \(max(A,B)\)

额外考虑环的情况:

  • 奇环的话,就会出现正向正向,所以如果一个图内包含奇环,整张图的贡献清零就行
  • 偶环的话,照样可以标记黑白黑白,无影响
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
const int N=2e5+5;
int head[N],nxt[N<<1],to[N<<1],tot;
inline void add(int x,int y){
    to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
    return;
}
int dep[N];
int ans=0;
bool vis[N];
bool flag=true;
int A=0,B=0;
inline void dfs(int now,int fa){
    if(vis[now]) return;
    vis[now]=true;
    dep[now]=dep[fa]+1;
    if(dep[now]&1){
        A++;
    }
    else{
        B++;
    }
    for(int i=head[now];i;i=nxt[i]){
        int y=to[i];
        if(dep[y]){
            int cnt=dep[now]-dep[y];
            if(!(cnt&1)){//ji环
                flag=false;
            }
            // else{
            //     A+=(cnt+1)/2;
            //     B+=(cnt+1)/2;
            // }
        }
        else{
            dfs(y,now);
        }
    }
}
inline void solve(){
    cin>>n>>m;
    tot=0;
    for(int i=1;i<=n;i++){
        dep[i]=0;
        head[i]=0;
        vis[i]=false;
    }
    for(int i=1;i<=m;i++){
        int u,v;
        cin>>u>>v;
        add(u,v),add(v,u);
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            flag=true;
            A=0,B=0;
            dfs(i,0);
            if(flag){
                ans+=max(A,B);
                // cerr<<ans<<" "<<i<<endl;
            }
        }
    }
    cout<<ans<<'\n';
    return;
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

E. Sum of Digits (and Again) 1749

image
image

也是一道十分巧妙的题。先统计好每个数字出现了几次以及总和,然后循环找第二个数 \(x\)(足够小),然后用规则补全后面,设为 tmp,判断一下出现次数够不够以及所有数总和减去 tmp 总和是不是等于 \(x\)。因为第一个数的总和生成的第二个数 \(x\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
inline int f(int x){
    int res=0;
    while(x){
        res+=x%10;
        x/=10;
    }
    return res;
}
inline void solve(){
    string s;
    cin>>s;
    int n=s.size();
    if(n==1){
        cout<<s<<'\n';
        return;
    }
    vector<int> cnt(10,0);
    int sum=0;
    for(int i=0;i<n;i++){
        cnt[s[i]-'0']++;
        sum+=(s[i]-'0');
    }
    for(int x=1;x<=9*n;x++){
        int ss=x;
        string tmp=to_string(ss);
        while(ss>9){
            ss=f(ss);
            tmp+=to_string(ss);
        }
        vector<int> cnt2(10,0);
        int sum2=0;
        for(int i=0;i<tmp.size();i++){
            cnt2[tmp[i]-'0']++;
            sum2+=(tmp[i]-'0');
        }
        // cerr<<x<<" "<<sum2<<endl;
        bool flag=true;
        for(int i=0;i<=9;i++){
            if(cnt[i]<cnt2[i]){
                flag=false;
                // cerr<<i<<" "<<flag<<endl;
            }
        }
        // if(flag&&sum==sum2&&cnt[0]==cnt2[0]){
        //     cout<<tmp<<'\n';
        //     return;
        // }
        if(sum-sum2!=x){
            flag=false;
        }
        // cerr<<x<<" "<<sum<<" "<<sum2<<" "<<flag<<endl;
        if(flag){
            // cerr<<"???????????????????????\n";
            for(int i=9;i>=0;i--){
                while(cnt[i]-cnt2[i]>0){
                    cout<<i;
                    cnt[i]--;
                }
            }
            cout<<tmp<<'\n';
            return;
        }
    }
    return;
}
signed main(void){
    cin.tie(NULL)->sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

F. Sum of Fractions 2222

暂无

G. Grid Path error

暂无

posted @ 2026-03-19 00:53  超绝最可爱天使酱  阅读(0)  评论(0)    收藏  举报