zoukankan      html  css  js  c++  java
  • 【考试反思】联赛模拟测试12

    震惊,我居然不会数细胞。

    菜的离谱。

    T1: 100 ( ightarrow) 80

    T4: 20 ( ightarrow) 0

    T1:松鼠的新家

    震惊,我建边居然不开二倍,那没事了。

    树上差分裸题。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=3e5+10;
    int n;
    int a[maxn];
    
    struct Edge{
        int from,to,nxt;
    }e[maxn];//这样直接80 = =
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int head[maxn],cnt;
    inline int add(int u,int v){
        e[++cnt].from=u;
        e[cnt].to=v;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }
    
    int fa[maxn],dep[maxn],siz[maxn],son[maxn];
    void dfs1(int u){
        dep[u]=dep[fa[u]]+1;siz[u]=1;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(v==fa[u])continue;
            fa[v]=u;dfs1(v);
            siz[u]+=siz[v];
            if(!son[u]||siz[v]>siz[son[u]])son[u]=v;
        }
    }
    
    int top[maxn];
    void dfs2(int u,int t){
        top[u]=t;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(v==fa[u])continue;
            dfs2(v,v==son[u]?t:v);
        }
    }
    
    inline int lca(int u,int v){
        while(top[u]!=top[v]){
            if(dep[top[u]]<dep[top[v]])swap(u,v);
            u=fa[top[u]];
        }
        return dep[u]<dep[v]?u:v;
    }
    
    int c[maxn];
    long long f[maxn];
    void dfs3(int u){
        f[u]=c[u];
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(v==fa[u])continue;
            dfs3(v);
            f[u]+=f[v];
        }
    }
    
    int main(){
    #ifndef LOCAL
        freopen("home.in","r",stdin);
        freopen("home.out","w",stdout);
    #endif
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        for(int i=1;i<n;i++){
            int u=read(),v=read();
            add(u,v);add(v,u);
        }
        dfs1(1);dfs2(1,1);
        for(int i=1;i<n;i++){
            int u=a[i],v=a[i+1],lcam=lca(u,v);
            c[u]++;c[v]++;c[lcam]--;
            if(fa[lcam])c[fa[lcam]]--;
        }
        dfs3(1);
        for(int i=2;i<=n;i++)
            f[a[i]]--;
        for(int i=1;i<=n;i++)
            printf("%lld
    ",f[i]);
        return 0;
    }
    

    谢谢,已经在学了

    T2:trade

    震惊,贪心题我居然 DP,直接挂掉。

    震惊,第二维限制到 500 之后直接 AC。

    震惊,天皇震怒直接卡掉

    乱搞的 Code
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    int n;
    int a[maxn];
    int f[2][maxn];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int main(){
    #ifndef LOCAL
        freopen("trade.in","r",stdin);
        freopen("trade.out","w",stdout);
    #endif
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        memset(f,-0x3f,sizeof(f));
        f[0][0]=0;
        int k=0;
        for(register int i=1;i<=n;i++){
            k^=1;
            for(register int j=0;j<min(i,500);j++){
                f[k][j]=max(f[k][j],f[k^1][j]);
                f[k][j+1]=max(f[k][j+1],f[k^1][j]-a[i]);
                if(j)f[k][j-1]=max(f[k][j-1],f[k^1][j]+a[i]);
            }
        }
        printf("%d
    ",f[k][0]);
        return 0;
    }
    

    所以正解居然是反悔贪心。

    可能大家都能想到贪心,那就是从后选最大的,从前选最小的。那么如何维护?

    用一个小根堆来维护所有的 (a[i]),如果队列为空或当前的 (a[i]) 小于等于堆顶,直接入队。否则,买入堆顶并在现在卖出,弹出堆顶,压入当前的两个货物。

    由于我们使用小根堆,所以当前堆顶一定是之前最小的买入价值。所以我们买入堆顶的策略肯定是最优的。但在今天卖出却不一定是最优的,因为之后可能出现更大的 (a[i])。所以我们要用可撤销贪心。

    假设我们之后会用一次撤销操作,那么设堆顶的货物价值为 (a),今天卖出的价值为 (b),之后最优日卖出的价值为 (c)。因为我们之前取了 (b-a) 作价值,然而事实上最优策略是 (c-a)。所以我们只需要再取一个 (c-b) 作价值即可。这就是压入的第一个货物。至于第二个货物,由于实际上当前策略由 (b-a) 转换成了 (c-a) 了,所以在价值为 (b) 的那天就不必卖东西了,那么就可以选择在当日购入物品了,第二个货物就对应这个物品。

    更加严谨的证明

    正确的 Code
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    int n;
    long long ans;
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    priority_queue<int,vector<int>,greater<int> > q;
    
    int main(){
    #ifndef LOCAL
        freopen("trade.in","r",stdin);
        freopen("trade.out","w",stdout);
    #endif
        n=read();
        for(int i=1;i<=n;i++){
            int a=read();
            if(q.empty()||a<=q.top())q.push(a);
            else{
                ans+=a-q.top();q.pop();
                q.push(a);q.push(a);
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
    

    T3:sum

    震惊,这题居然是莫队。

    其实前面的两个特殊性质都是提示。既然要用莫队,我们就看 (S_n^m) 如何转移。

    (n) 相等时,显然:

    [S_n^m=S_n^{m-1}+C_n^m ]

    [S_n^{m-1}=S_n^m-C_n^m ]

    (m) 相等时,我们可以画一个杨辉三角。例如:

      1 3 3
    1 4 6
    

    设下面一行就是第 (n) 行,上面一行是 (n-1) 行,我们都取 (m) 个。那么依据小学知识可知 1=14=1+36=3+3。我们发现,在 (n-1) 行转移到 (n) 行的过程中,除了第 (m) 项,其他的都转移了两次。那么我们显然可以得到:

    [S_n^m=2 imes S_{n-1}^m-C_{n-1}^m ]

    [S_{n-1}^m=cfrac{S_n^m+C_{n-1}^m}{2} ]

    由于题目可离线,利用这四个 (O(1)) 转移的式子,我们可以利用莫队解决问题。注意由于 (ngeq m) ,所以 (m) 为左端点,(n) 为右端点(震惊,好像反过来也可以)。块长取 (sqrt{max n})

    Code
    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int Mod=1e9+7;
    const int maxn=1e5+10;
    int Max,T,S,ans=1;//ans别初始化成0了...
    int res[maxn];
    
    struct Node{
        int n,m,id;
        friend inline bool operator <(register const Node& A,register const Node& B){
            return (A.m/S)^(B.m/S)?A.m<B.m:(((A.m/S)&1)?A.n<B.n:A.n>B.n);
        }
    }q[maxn];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int fac[maxn],inv[maxn];
    inline void Init(){
        fac[0]=fac[1]=inv[0]=inv[1]=1;
        for(int i=2;i<=1e5;i++)
            inv[i]=(Mod-Mod/i)*inv[Mod%i]%Mod;
        for(int i=2;i<=1e5;i++){
            fac[i]=fac[i-1]*i%Mod;
            inv[i]=inv[i-1]*inv[i]%Mod;
        }
    }
    
    inline int C(int n,int m){
        if(n<m)return 0;
        if(!m||n==m)return 1;
        return fac[n]*inv[m]%Mod*inv[n-m]%Mod;
    }
    
    signed main(){
    #ifndef LOCAL
        freopen("sum.in","r",stdin);
        freopen("sum.out","w",stdout);
    #endif
        read();Init();
        T=read();
        for(int i=1;i<=T;i++){
            int n=read(),m=read();
            if(n>Max)Max=n;
            q[i]=(Node){n,m,i};
        }
        S=sqrt(Max);
        sort(q+1,q+T+1);
        int m=1,n=0;//莫队左端点建议从1开始,否则有的题会挂,比如小Z的袜子
        for(int i=1;i<=T;i++){
            int nowm=q[i].m,nown=q[i].n;
            while(n<nown)ans=(2*ans%Mod-C(n++,m)+Mod)%Mod;
            while(m>nowm)ans=(ans-C(n,m--)+Mod)%Mod;
            while(n>nown)ans=(ans+C(--n,m))%Mod*inv[2]%Mod;
            while(m<nowm)ans=(ans+C(n,++m))%Mod;
            res[q[i].id]=ans;
        }
        for(int i=1;i<=T;i++)
            printf("%lld
    ",res[i]);
        return 0;
    }
    

    T4:building

    震惊,我居然不会数细胞。已经开始学队列和栈了。

    神奇模拟,正在努力。

  • 相关阅读:
    20210108 android学习
    20210106 android学习
    20210105 android学习
    20210104 android学习
    httpClient请求转发个人理解
    CefSharp如何判断页面是否加载完
    C#/WPF 使用的Task线程程序缺依旧响应缓慢问题
    修改LiveChart的提示显示位置
    Wpf双击放大缩小界面
    WPF设置无边框CefSharp显示不出来问题
  • 原文地址:https://www.cnblogs.com/Midoria7/p/13784857.html
Copyright © 2011-2022 走看看