2022 HDU多校6

Maex

Problem

给定一个大小为\(n\)的树,每个节点有一个权值\(a_i\),并且保证不存在两个点的点权相同,定义\(p_u=\text{mex}\{x|v\in subtree\left(u\right),x=a_i\}\)。现在你可以给这些点赋予点权,使得\(\sum_{i=1}^np_i\)最大,请求出这个值。

\(1\le n\le 5\times 10^5\)

Solve

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

根据这个,考虑一个节点\(u\)\(p_u\)不为\(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\)个任务,每个任务可以用\((s_i,d_i)\)来表示,现在有\(q\)个询问,每次询问给定一个数\(x\),问有多少个任务满足\(\exist j,0\le j\le d_i\)\(s_ia^j\equiv x\mod{P}\),其中\(a,P\)题目给定。

\(1\le n,q\le 1000\)\(0\le d_i\le 200000\)

Solve

问题转变就是有多个任务,满足\(\exist y\le d_i\),使得\(s_ia^y\equiv x\mod{P}\),变形一下就是\(a^y\equiv xs^{-1}\mod{P}\)。所以可以求出每个\(a^i \% P\)的值,然后记录可以变成这个值的最小的天数\(d\),如果一个任务的\(d_i\)大于这个最小的\(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\),取垂直的两条边作基向量即可,可以得到两个等式

\[\overrightarrow{AP}=\lambda \overrightarrow{AB}+\mu \overrightarrow{AD}\\ \\overrightarrow{aP}=\lambda \overrightarrow{ab}+\mu \overrightarrow{ad} \]

然后可以得到

\[\overrightarrow{OP}=\overrightarrow{OA}+\overrightarrow{AP}\\ \overrightarrow{OP}=\overrightarrow{Oa}+\overrightarrow{aP}\\ \]

作差可以得到

\[\lambda (\overrightarrow{AB}-\overrightarrow{ab})+\mu (\overrightarrow{AD}-\overrightarrow{ad})=\overrightarrow{Oa}-\overrightarrow{OA} \]

然后对应分量相等得到两个方程组解\(\lambda\)\(\mu\)即可

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

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

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 @ 2022-08-29 16:56  Arashimu  阅读(28)  评论(0编辑  收藏  举报