zoukankan      html  css  js  c++  java
  • 2020 wannafly camp 补题 day1

    题目可以从牛客上找到。

    最简单的一个题应该是B

    B. 密码学

    这个应该就是倒着推,题目给了你加密的顺序,所以我们逆推这个就可以得到每一次加密前的字符串。

    1H. 最大公约数

    题目大意就是给你一个范围1~n 满足gcd(k,y) 是独一无二的,意思是除了k之外,1~n中没有gcd(i,y)==gcd(k,y)

    这个题目首先要知道gcd(k,y)==k,因为如果等于x,且x<k,那么因为x属于1~n,所以gcd(x,y)==x

    这样就不满足题目要求了。

    所以y一定是k的倍数,因为在这n个数中,可能有很多数都是k的整数倍,那么这个y要满足要求,则必须是

    gcd(m*k,y)!=k,所以如果有数是k的素数倍,那么y也一定是这个素数的倍数。

    这个会爆long long 。

    F. 乘法

    这个算法是二分,二分这个第k大,然后check。

    这个check有点难写,因为有正有负需要分开写。

    最后对我来说比较坑的地方是因为取过膜,所以不可以直接除以2,需要乘以2的逆元

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<bits/stdc++.h>
    #define inf64 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn=1e5+10;
    typedef long long ll;
    ll a[maxn],b[maxn];
    ll a1[maxn],a2[maxn],b1[maxn],b2[maxn];
    ll tota1=0,tota2=0,totb1=0,totb2=0,zera=0,zerb=0;
    ll n,m,k;
    bool check(ll x){
        ll sum=0;
        if(x<=0){
            sum+=tota1*totb1+tota2*totb2+zera*m+zerb*n-zera*zerb;
    //        printf("sum=%lld
    ",sum);
            if(x==0) return sum>=k;
            if(sum>=k) return true;
            x=-x;
            for(int i=1;i<=tota1&&totb2;i++){
                ll t=upper_bound(b2+1,b2+1+totb2,x/a1[i])-b2;
                if(b2[t]>x/a1[i]||t>totb2) t--;
                sum+=t;
    //            printf("aa i=%d sum=%lld t=%d %d
    ",i,sum,t,totb2);
            }
            for(int i=1;i<=tota2&&totb1;i++){
                ll t=upper_bound(b1+1,b1+1+totb1,x/a2[i])-b1;
    //            printf("t=%d
    ",t);
                if(b1[t]>x/a2[i]||t>totb1) t--;
                sum+=t;
    //            printf("i=%d sum=%lld t=%d
    ",i,sum,t);
                
            }
    //        printf("x=%lld sum=%lld
    ",x,sum);
            return sum>=k;
        }
        for(int i=1;i<=tota1&&totb1;i++){
            ll val=x/a1[i];
            if(x%a1[i]) val++;
            int t=lower_bound(b1+1,b1+1+totb1,val)-b1;
            sum+=totb1-t+1;
        }
        for(int i=1;i<=tota2&&totb2;i++){
            ll val=x/a2[i];
            if(x%a2[i]) val++;
            int t=lower_bound(b2+1,b2+1+totb2,val)-b2;
            sum+=totb2-t+1;
        }
        return sum>=k;
    }
    int main(){
        scanf("%lld%lld%lld",&n,&m,&k);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        for(int i=1;i<=m;i++) scanf("%lld",&b[i]);
        for(int i=1;i<=n;i++){
            if(a[i]<0) a1[++tota1]=-a[i];
            else if(a[i]>0) a2[++tota2]=a[i];
            else zera++;
        }
        for(int i=1;i<=m;i++){
            if(b[i]<0) b1[++totb1]=-b[i];
            else if(b[i]>0) b2[++totb2]=b[i];
            else zerb++;
        }
        sort(a1+1,a1+1+tota1);
        sort(a2+1,a2+1+tota2);
        sort(b1+1,b1+1+totb1);
        sort(b2+1,b2+1+totb2);
    //    printf("%d %d %d %d
    ",tota1,tota2,totb1,totb2);
        ll l=-inf64,r=inf64,ans=0;
        while(l<=r){
            ll mid=(l+r)>>1;
            if(check(mid)) ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    /*
    1 2 2
    2
    -3 -3
    
    */
    F

    A期望逆序对

    这个是要求逆序对尽可能少,虽然说是期望,但其实就是通过对这些区间进行重新排序,然后减少逆序对。

    这是一个贪心题目,如何减少逆序对呢,这个可以通过排序,不过这个排序方式有点难想。

    然后就是求期望。

    最后很坑的一点是这个题目只可以n*n,卡n*n*logn

    而逆元的求法就是一个logn的复杂度。

    7-3 1C. 染色图

    这个染色图题目不是很难,但是要知道数论分块这个知识点。

    数论分块是解决:

    这个问题的。复杂度是o(n)

    写法很简单

    int ans = 0;
    for(int l = 1, r = 0; l <= n; l++) {
        r = n / (n / l);
        // do something
    }
    

      

    知道这个之后推一下这个公式就可以算出答案,这个公式很好推的。

    推公式:

    假设只有三种颜色,A颜色有x个点,B颜色有y个点,C颜色有z个点。

    则 x+y+z=n  一共可以连的边是  [x*(n-x)+y*(n-y)+z*(n-z)]/2

    可以推出时 [n*n-x*x-y*y-z*z]/2

    因为x+y+z=n 所以如果 x=y=z=n/3 则是最少的边。

    然后就可以转化成数论分块要解决的问题。

    这些是在camp补的题目,具体代码等回家再补。

    E 树与路径

     在家里补了一下这个E题,这个是看着题解补的,还看了标程才看懂。

    这个题目类似于树上差分,不过比裸的差分要难很多。

    设 Ai 为以 i 为根节点,所有路径的权值和。

    考虑两种情况:

    1) 当 i 和 fi 在同一条路径上时,是不是 Ai=Afi

    2) 当 i 和 fi 不在同一条路径上的时候,假设i 往下走的长度为 x,这条路径的长度为s

    所以Ai=x*(s-x)  Afi=(x+1)*(s-x-1)

    所以 Ai-Afi=2*x+1-s

    所以对于路径 (u,v)来说,设k为u,v的最近公共祖先,这条路对于 Au的贡献是1-s,对于Afu的贡献是 3-s

    所以可以求出在u->k 这条路径的 i 来说贡献就是 2*(du-di)+1-s

    所以就是 -2*di+2*du+1-s,因为1+2*du-s是一个定值,所以可以设为常数 b,di是一个变化的量,-2也是一个定值设为a

    所以就是 a*di+b,因为 a1*di+b1+a2*di+b2=(a1+a2)*di+b1+b2

    所以就可以直接合并求解。因为在两个根节点的位置a和b都加了一部分,所以需要在根节点的位置减去这一部分,这个就是树上差分的思想。

    因为求的是边,所以在根节点把所有加上去的都可以直接减去。

    写完了,这个题目我觉得比较难的地方就是去分情况讨论,

    因为这个由得到一个点去推其他点的这个套路应该是可以猜得到的,但是怎么去分情况呢,这个我就有点迷茫。

    一开始思路很乱,然后看了题解之后决定豁然开朗,不过知道这个情况怎么分之后,就要推这个递推式了。

    递推式推出来的方法很多,也有很多不同的变形,但是对于这个题目来说,如果这个递推式唯一的变量就是这个深度,

    那么就可以直接进行叠加,就很好写了。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #include <cmath>
    #include <bitset>
    #include <vector>
    #include <map>
    #include <string>
    #include <cstring>
    #define fir first
    #define sec second
    using namespace std;
    typedef long long ll;
    const int maxn=3e5+10;
    int n,m;
    ll a[maxn],b[maxn],ans[maxn],d[maxn];
    struct node{
        int v,nxt;
        node(int v=0,int nxt=0):v(v),nxt(nxt){}
    }e[maxn*2];
    bool vis[maxn*2];
    int head[maxn],tot,cnt;
    int ver[maxn*2],first[maxn*2],deep[maxn*2];
    void add(int u,int v){
        e[++cnt]=node(v,head[u]);
        head[u]=cnt;
        e[++cnt]=node(u,head[v]);
        head[v]=cnt;
    }
    
    int min_(int l, int r) {
        return deep[l] < deep[r] ? l : r;
    }
    
    void dfs(int u, int dep) {
    //    printf("u=%d dep=%d
    ",u,dep);
        vis[u] = 1;ver[++tot] = u;
        deep[tot] = dep;first[u] = tot;
        for (int i = head[u]; i != -1; i = e[i].nxt) {
            int v = e[i].v;
            if (!vis[v]) {
                d[v]=d[u]+1;
                dfs(v, dep + 1);
                ver[++tot] = u;deep[tot] = dep;
            }
        }
    }
    int dp[maxn*2][20];
    void RMQ(int n) {
        for (int i = 1; i <= n; i++) dp[i][0] = i;
        for (int j = 1; (1 << j) <= n; j++) {
            for (int i = 1; i + (1 << j) - 1 <= n; i++) {
                dp[i][j] = min_(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    int LCA(int x, int y) {
        int l = first[x];
        int r = first[y];
    //    printf("l=%d r=%d
    ",l,r);
        if (l > r) swap(l, r);
        int k = (log(r - l + 1.0)) / (log(2.0));
        int id = min_(dp[l][k], dp[r - (1 << k) + 1][k]);
        return ver[id];
    }
    void dfs2(int u,int pre){
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].v;
            if(v==pre) continue;
            dfs2(v,u);
            a[u]+=a[v];
            b[u]+=b[v];
        }
    }
    void dfs3(int u,int pre){
        ans[u]+=a[u]*d[u]+b[u];
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].v;
            if(v==pre) continue;
            ans[v]=ans[u];
            dfs3(v,u);
        }
    }
    int main(){
        tot=0,cnt=0;
        memset(head,-1,sizeof(head));
        memset(d,0,sizeof(d));
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        dfs(1,0);
        RMQ(2*n-1);
        while(m--){
            int u,v;
            scanf("%d%d",&u,&v);
            int k=LCA(u,v);
    //        printf("u=%d v=%d k=%d
    ",u,v,k);
            int s=d[u]+d[v]-2*d[k];
            a[u]-=2,b[u]+=1+2*d[u]-s;
            a[v]-=2,b[v]+=1+2*d[v]-s;
            a[k]+=4,b[k]-=1+2*d[u]-s+1+2*d[v]-s;
            int x=d[u]-d[k];
            ans[1]+=x*1ll*(s-x);
    //        printf("s=%d x=%d
    ",s,x);
        }
    //    printf("s %lld
    ",ans[1]);
        dfs2(1,-1),dfs3(1,-1);
        for(int i=1;i<=n;i++) printf("%lld
    ",ans[i]);
        return 0;
    }
    E

    差不多先补到这里,day1 还有一些AC代码没有贴,以后再写吧。

  • 相关阅读:
    12-五子棋游戏:享元模式
    11-制作糖醋排骨:外观模式
    10-蒸馒头:装饰者模式
    09-公司层级结构:组合模式
    08-开关与电灯:桥接模式
    07-电源转换:适配器模式
    将博客搬至CSDN
    iview和element中日期选择器快捷选项的定制控件
    详解AJAX工作原理以及实例讲解(通俗易懂)
    最全肌肉锻炼动图
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/12194433.html
Copyright © 2011-2022 走看看