2022 HDU多校6

Maex

Problem

给定一个大小为n的树,每个节点有一个权值ai,并且保证不存在两个点的点权相同,定义pu=mex{x|vsubtree(u),x=ai}。现在你可以给这些点赋予点权,使得i=1npi最大,请求出这个值。

1n5×105

Solve

  • hit1:保证不存在两个点的点权相同

根据这个,考虑一个节点upu不为0,当且仅当它的子树里面出现过点权为0的节点,而这个点权为0的节点当且仅有一个,然后一直往这个子树里面找,发现p不为0的点一定形成一条链,并且这条链上的节点的p就是这个节点子树的大小,因为考虑这个子树中不存在0权值点的那些链,发现他们的贡献一定是0,那我们直接按照顺序赋值可以最优,所以直接dfs一直往下搜索找到最大值即可

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin>>T;
    while(T--){
        int n;
        cin>>n;
        vector<int>sz(n+1);
        vector<vector<int>>E(n+1);
        for(int i=1;i<n;i++){
            int u,v;
            cin>>u>>v;
            E[u].push_back(v);
            E[v].push_back(u);
        }
        auto dfs1=[&](auto self,int u,int fa)->void{
            sz[u]=1;
            for(auto v:E[u]){
                if(v==fa) continue;
                self(self,v,u);
                sz[u]+=sz[v];
            }
            sort(E[u].begin(),E[u].end(),[&](int i,int j){return sz[i]>sz[j];});
        };
        dfs1(dfs1,1,1);
        ll ans=0;
        auto dfs2=[&](auto self,int u,int fa,ll w)->void{
            ans=max(ans,w);
            for(auto v:E[u]){
               if(v==fa) continue;
               self(self,v,u,w+sz[v]);
            }
        };
        dfs2(dfs2,1,1,n);
        cout<<ans<<'\n';
    }
}

Shinobu loves trip(数论、卡常)

Problem

n个任务,每个任务可以用(si,di)来表示,现在有q个询问,每次询问给定一个数x,问有多少个任务满足j,0jdisiajxmodP,其中a,P题目给定。

1n,q10000di200000

Solve

问题转变就是有多个任务,满足ydi,使得siayxmodP,变形一下就是ayxs1modP。所以可以求出每个ai%P的值,然后记录可以变成这个值的最小的天数d,如果一个任务的di大于这个最小的d,那么就可以达到。

Code

#include <iostream>
#include <unordered_map>
#define ll long long
using namespace std;
const int N=10005;
const int D=200005;
int P,a,n,q;
int s[N],d[N],apw[D],inv[N];
inline int power(int x,int y){
    int res=1;
    while(y){
        if(y&1) res=(ll)res*x%P;
        x=(ll)x*x%P;
        y>>=1;
    }
    return res;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin>>T;
    while(T--){
      cin>>P>>a>>n>>q;
      int mx=0;
      for(int i=1;i<=n;i++){
        cin>>s[i]>>d[i];
        mx=max(mx,d[i]);
        inv[i]=power(s[i],P-2);
      }
      unordered_map<int,int>hs;
      apw[0]=1;
      hs[apw[0]]=0;
      int limit=min(mx,P-2);
      for(int i=1;i<=limit;i++){
         apw[i]=(ll)apw[i-1]*a%P;
         if(hs.find(apw[i])==hs.end()){
            hs[apw[i]]=i;
         }
     }
      int ans;
      while(q--){
        int x;
        cin>>x;
        ans=0;
        if(x==0){
            for(int i=1;i<=n;i++){
                if(s[i]==0) ans++;
            }
            cout<<ans<<'\n';
            continue;
        }
        for(int i=1;i<=n;i++){
            if(s[i]==0) continue;
            int t=(ll)x*inv[i]%P;
            if(hs.count(t) && hs[t]<=d[i]) ans++;
        }
        cout<<ans<<'\n';
      }
    }
    return 0;
}

Map(计算几何、巴拿赫不动点)

Problem

给定二维平面中两个相似矩形,保证小矩形包含在大矩形中,问不动点坐标(巴拿赫不动点)

Solve

我们先把一个地图看做一个直角坐标系,在这个坐标系里面表示不动点点P,取垂直的两条边作基向量即可,可以得到两个等式

AP=λAB+μADoverrightarrowaP=λab+μad

然后可以得到

OP=OA+APOP=Oa+aP

作差可以得到

λ(ABab)+μ(ADad)=OaOA

然后对应分量相等得到两个方程组解λμ即可

Code

乘法运算的时候不要用double,因为这个一直T

#include <bits/stdc++.h>
#define ll long long
using namespace std;
struct Point{
    int x,y;
    Point operator + (const Point&t)const{
        return {x+t.x,y+t.y};
    }
    Point operator - (const Point&t)const{
        return {x-t.x,y-t.y};
    }
}M[5],m[5];
typedef Point Vector;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout<<fixed<<setprecision(6);
    int T;
    cin>>T;
    while(T--){
        for(int i=1;i<=4;i++){
            int x,y;
            cin>>x>>y;
            M[i]={x,y};
        }
        for(int i=1;i<=4;i++){
            int x,y;
            cin>>x>>y;
            m[i]={x,y};
        }
        Vector AB=M[2]-M[1],AD=M[4]-M[1];
        Vector ab=m[2]-m[1],ad=m[4]-m[1];
        Vector Oa=m[1],OA=M[1];
        int x1=(AB-ab).x,y1=(AB-ab).y;
        int x2=(AD-ad).x,y2=(AD-ad).y;
        int x=(Oa-OA).x,y=(Oa-OA).y;
        double t1=(x*y2-y*x2)/(double)(x1*y2-x2*y1);
        double t2=(x*y1-y*x1)/(double)(x2*y1-x1*y2);
        double Px=OA.x+AB.x*t1+AD.x*t2,Py=OA.y+AB.y*t1+AD.y*t2;
        cout<<Px<<" "<<Py<<'\n';  

    }
    return 0;
}

Planar graph(图论、最大生成树)

Problem

给定一个平面图。平面图围被若干条边分成若干个平面。把每个平面看做城市,你需要在边上建桥,使得城市之间可以互通。问最少需要建多少个桥,可以使得每个面之间可以相互到达,并且要求建立桥的边的字典序最小。

Solve

建立桥可以看做是断边操作,发现断完边之后就图上是没有环的,变成了一棵树,而要求断开的边字典序要最小,所以按照把边的序号当做边权,求一个最大生成树,把不在树中的边加入答案即可

Code

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
struct edges{
    int u,v;
}e[N];
int f[N];
int find(int x){
    return f[x]==x?x:f[x]=find(f[x]);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin>>T;
    while(T--){
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++) f[i]=i;
        for(int i=1;i<=m;i++){
            int u,v;
            cin>>u>>v;
            e[i]={u,v};
        }
        vector<int>ans;
        for(int i=m;i>=1;i--){
           int u=e[i].u,v=e[i].v;
           int fu=find(u),fv=find(v);
           if(fu==fv){
             ans.push_back(i);
             continue;
           }
           f[fu]=fv;
        }
        cout<<ans.size()<<'\n';
        reverse(ans.begin(), ans.end());
        for(auto x:ans) cout<<x<<" ";
        cout<<'\n';
    }
}

Find different(数论、群论)

Loop(贪心)

Problem

给定一个长度为n的序列,可以进行k次操作,每次操作可以选择一个区间[L,R],然后把这个区间的数循环左移1位。问最后可以得到的最大字典序的序列是多少

Solve

字典序最大,就是前面的数字尽可能大,降序尽可能多。操作可以看做每次可以把一个前面的数放在后面的任意位置,显然,我们要尽可能把前面小的数字放到后面。比如如果存在ai1<ai,那么ai1可能放到后面会更好,然后继续检查ai2,而我们要求字典序最大,所以从前面往后检查是更优的,所以每次可以放就一定放到后面并且消耗一次操作,知道无法操作就停止检查,这时我们可以得到一个保留序列和删除序列,然后把删除序列中的数大贪心地插入到保留序列里面即可

Code

#include <bits/stdc++.h>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin>>T;
    while(T--){
        int n,k;
        cin>>n>>k;
        vector<int>a(n+1);
        for(int i=1;i<=n;i++) cin>>a[i];
        vector<int>rem;
        priority_queue<int> del;
        for(int i=1;i<=n;i++){
            while(rem.size() && rem.back()<a[i] &&k){
                del.push(rem.back());
                rem.pop_back();
                k--;
            }
            rem.push_back(a[i]);
        }
        vector<int>b;
        rem.push_back(-1e9);
        del.push(-1e9);
        int id=0;

        while(int(b.size())<n){
            if(rem[id]>=del.top()) b.push_back(rem[id++]);
            else b.push_back(del.top()),del.pop();
        }

        for(int i=0;i<n;i++){
            cout<<b[i];
            if(i!=n-1) cout<<" ";
            else cout<<"\n";
        }
    }
}
posted @   Arashimu  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示