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代码没有贴,以后再写吧。

原文地址:https://www.cnblogs.com/EchoZQN/p/12194433.html