20241215北京总结

总结

做题 :

贪心:\(ACHIJ\) ; 博弈:\(AC\) ; 构造:无

授课内容 :

贪心 : 拟阵 , 反悔贪心

博弈 : SG , 非SG ( 感觉就是思维 )

构造 : 思维+1 人类智慧

未理解 :

拟阵 , 部分博弈 , 部分构造

练习题总结 : 如下

模拟赛 :

\(THUPC2025\) 初赛

\(+6 , rk360\)

\(M\) 显然 , 我开的 \(J\)

首先我们把所有黑点旁边的点叫做需保护的点 , 我们要求需保护的点旁边全都有白点 , 由于黑点不能染成白点 , 我们用黑点把树分成几份

对于每一份 , 贪心的做每一个需保护点的爹 , 但是有的点 , 它没有爹 , 怎么办呢

先处理其他的点 , 若这种没爹的点被保护就无所谓了 , 没被保护就答案加一.

后来开\(D\) , 发现 \(D\) 让我想复杂了 , 只需要直接维护到贡献开始循环就可以了 , 其中不断取最大值最小值显然平衡树 , \(set\) 实现

其他题队友做的 , 以后补 /kk

练习题总结如下

贪心

上来先做了三道反悔贪心\(/kk\)

P1484 种树

先从大到小选 , 如果只能选两个的话 , 要么选 \(a_i\) 与不和它相邻的\(a_j\) , 要么选 \(a_{i-1}\)\(a_{i+1}\) , 选了 \(a_i\) 就把 \(a_{i-1}+a_{i+1}-a_i\) 入堆就行 , 维护一个链表 .

/*
 * @Author: 2019yyy
 * @Date: 2024-12-15 08:22:59
 * @LastEditors: 2019yyy
 * @LastEditTime: 2024-12-15 08:48:23
 * @FilePath: \code\20241215\P1484.cpp
 * @Description: 
 * 
 * I love Chtholly forever 
 */
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct Node{
    int val,id;
    bool operator<(Node another) const {
        return val<another.val;
    }
} a[1100000];
int v[1100000];
priority_queue<Node> q;
bool vis[1100000];
int nxt[1100000],pre[1100000];
signed main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i];
        nxt[i]=i+1;
        pre[i]=i-1;
        q.push({v[i],i});
    }
    int ans=0,cnt=0;
    while(not q.empty()){
        Node now=q.top();
        q.pop();
        if(vis[now.id]){
            continue;
        }
        cnt++;
        if(now.val<0 or cnt>m){
            break;
        }
        ans+=now.val;
        vis[pre[now.id]]=vis[nxt[now.id]]=true;
        v[now.id]=now.val=v[pre[now.id]]+v[nxt[now.id]]-now.val;
        pre[now.id]=pre[pre[now.id]];
	    nxt[now.id]=nxt[nxt[now.id]];
	    pre[nxt[now.id]]=now.id;
	    nxt[pre[now.id]]=now.id;
        q.push(now);
    }
    cout<<ans<<'\n';
    return 0;
}

数据备份与本题思路基本相同 , 这里不过多赘述 .

[AGC008B] Contiguous Repainting

因为填色次数是无限的 , 所以你前面想怎么填怎么填 , 只要保证有一段是连续长度至少为 \(k\) 的区间就行

枚举这个区间

/*
 * @Author: 2019yyy
 * @Date: 2024-12-15 17:49:16
 * @LastEditors: 2019yyy
 * @LastEditTime: 2024-12-15 17:59:42
 * @FilePath: \code\20241215\agc008b.cpp
 * @Description: 
 * 
 * I love Chtholly forever 
 */
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[1100000],sumn,summ;
signed main(){
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]>=0){
            sumn+=a[i];
            summ+=a[i];
        }
    }  
    for(int i=1;i<=k;i++){
        if(a[i]>=0){
            sumn-=a[i];
        }
        if(a[i]<0){
            summ+=a[i];
        }
    }
    int ans=max(sumn,summ);
    for(int i=k+1;i<=n;i++){
        if(a[i-k]>=0){
            sumn+=a[i-k];
        }
        if(a[i-k]<0){
            summ-=a[i-k];
        }
        if(a[i]>=0){
            sumn-=a[i];
        }
        if(a[i]<0){
            summ+=a[i];
        }
        ans=max(ans,max(sumn,summ));
    }
    cout<<ans<<'\n';
    return 0;
}

打怪兽与邻项交换法

\(𝑛\) 只怪兽, 初始你有 \(𝑘\) 点血量, 打第 \(𝑖\) 个怪兽至少需要 \(𝑎_𝑖\) 的血量, 打完第 \(𝑖\) 个怪兽之后会掉 \(𝑏_𝑖\) 的血量,你可以按照任何顺序依次打所有怪兽, 问能否打完所有怪兽,
并给出一种方案.

考虑打怪兽的先后顺序影响的需要最少血量数 , 如相邻 \(i\)\(j\)

如果先打 \(𝑖\), 需要 \(max(𝑎_𝑖,𝑏_𝑖 + 𝑎_𝑗)\) 的血量 ;
如果先打 \(𝑗\), 需要 \(max(𝑎_𝑗,𝑏_𝑗 + 𝑎_𝑖)\) 的血量

也就是说 \(b_i+a_j\)小的优 , 这就是邻项交换

[AGC032E] Modulo Pairing

如果没有膜 \(m\) , 那么这个问题肯定是大加小

考虑两组匹配 \((x,y)\) , \((w,z)\)\(y > z\) , \(w+z\geq m\)

开始最大为 \(max(x+y , w+z-m)\) ; 交换后最大为 \(max(x+z , w+y-m)\)

显然变得不劣 , \(so\) 排个序 , 以某点为分解线 , 前面的按朴素组合后面的按朴素组合

某点怎么求呢 , 这个点向左移左边肯定减少 , 右边的也减少 , 左移途径的点也减少 , 这下肯定不劣

于是就越左越好 , 但太左了不合法 , 所以在合法范围内左倾 , 容易想到二分

/*
 * @Author: 2019yyy
 * @Date: 2024-12-15 18:32:14
 * @LastEditors: 2019yyy
 * @LastEditTime: 2024-12-15 18:54:48
 * @FilePath: \code\20241215\agc032e.cpp
 * @Description: 
 * 
 * I love Chtholly forever 
 */
#include<bits/stdc++.h>
using namespace std;
int a[2100000];
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n*2;i++){
        cin>>a[i];
    }
    sort(a+1,a+n*2+1);
    int l=-1,r=n+1;
    while(l+1!=r){
        int mid=(l+r)>>1;
        bool flag=true;
        for(int i=n*2;i>=n*2-mid+1;i--){
            if(a[i]+a[n*4-2*mid-i+1]<m){
                flag=false;
                break;
            }
        }
        if(flag){
            l=mid;
        }else{
            r=mid;
        }
    }
    int ans=0;
    for(int i=1;i<=n-l;i++){
        ans=max(ans,a[i]+a[2*n-2*l+1-i]);
    }
    for(int i=n*2;i>=n*2-l+1;i--){
        ans=max(ans,(a[i]+a[n*4-2*l+1-i])%m);
    }
    cout<<ans<<'\n';
    return 0;
}

拟阵装备购买

喜报

我不会

博弈

\(Nim\) ,鉴定为纯纯的打表

poj2425

就是跑一边求 \(SG\)

/*
 * @Author: 2019yyy
 * @Date: 2024-12-15 19:22:35
 * @LastEditors: 2019yyy
 * @LastEditTime: 2024-12-15 19:56:32
 * @FilePath: \code\20241215\poj2425.cpp
 * @Description: 
 * 
 * I love Chtholly forever 
 */ 
#include<bits/stdc++.h>
using namespace std;
struct Edge{
    int next,to;
} a[1100000];
int cnt,head[5100];
int sg[5100];
int dfs(int x){
    if(sg[x]>=0){
        return sg[x];
    }
    vector<int> v;
    for(int i=head[x];i;i=a[i].next){
        int to=a[i].to;
        v.push_back(dfs(to));
    }
    sort(v.begin(),v.end());
    unique(v.begin(),v.end());
    int t=0;
    for(int i=0;i<v.size();i++){
        if(v[i]!=t){
            break;
        }
        t++;
    }
    return sg[x]=t;
}
void addEdge(int x,int y){
    a[++cnt].next=head[x];
    a[cnt].to=y;
    head[x]=cnt;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    while(cin>>n and n!=EOF){
        memset(sg,-0x3f,sizeof(sg));
        memset(head,0,sizeof(head));
        cnt=0;
        for(int i=1;i<=n;i++){
            int t;
            cin>>t;
            for(int j=1;j<=t;j++){
                int to;
                cin>>to;
                addEdge(i-1,to);
            }
        }
        for(int i=0;i<=n-1;i++){
            if(sg[i]<0){
                dfs(i);
            }
        }
        int m;
        while(cin>>m and m!=0){
            int sum=0;
            for(int i=1;i<=m;i++){
                int x;
                cin>>x;
                sum=sum^sg[x];
            }
            if(sum){
                cout<<"WIN\n";
            }else{
                cout<<"LOSE\n";
            }
        }
    }
    return 0;
}

看点非 \(Nim\) 博弈 , 感觉这种旨在思维

[AGC014D] Black and White Tree

首先考虑如果 \(Alice\) 最脑瘫 , 那么她一定要下到叶子 , 之后被 \(Bob\) 堵死

\(so\) , 我们作为不脑瘫的 \(Alice\) ,肯定是下到叶子的爹上 , 让\(Bob\) 填叶子 , 他要是脑瘫不填他就输了

所以一个爹对一个叶子 , 这是一个匹配 , 只要完全匹配 \(Bob\) 必赢 , 反之 \(Alice\) 必赢

怎么求一个树是否完全匹配呢 ? 从深度最深的点开始贪心 , 选爹肯定比选儿子要好 , 不断选爹匹配就好了

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
struct Edge{
    int next,to;
} a[2100000];
int cnt,head[1100000];
void addEdge(int x,int y){
    a[++cnt].next=head[x];
    a[cnt].to=y;
    head[x]=cnt;
}
int fa[1100000],dep[1100000];
void dfs(int x,int father){
    for(int i=head[x];i;i=a[i].next){
        int to=a[i].to;
        if(to==father){
            continue;
        }
        fa[to]=x;
        dep[to]=dep[x]+1;
        dfs(to,x);
    }
}
priority_queue<pii > q;
bool vis[1100000];
int main(){ 
    int n;
    cin>>n;
    for(int i=1;i<=n-1;i++){
        int x,y;
        cin>>x>>y;
        addEdge(x,y);
        addEdge(y,x);
    }
    if(n&1){
        cout<<"First\n";
        return 0;
    }
    dep[1]=1;
    dfs(1,0);
    for(int i=1;i<=n;i++){
        q.push(make_pair(dep[i],i));
    }
    vis[0]=true;
    while(not q.empty()){
        int now=q.top().second;
        q.pop();
        if(not vis[now]){
            vis[now]=true;
            if(vis[fa[now]]){
                cout<<"First\n";
                return 0;
            }
            vis[fa[now]]=true;
        }
    }
    cout<<"Second\n";
    return 0;
}

构造

诈骗

posted @ 2024-12-15 22:04  2019yyy  阅读(27)  评论(1)    收藏  举报