HGOI 20190310 题解

/*
又是又双叒叕WA的一天...
我太弱鸡了...
今天上午打了4道CF
*/

Problem 1 meaning

给出q组询问,求下列函数的值$ f(a) = \max\limits_{0 < b < a} \{   gcd(a\oplus b,a \ \& \ b)\} $

对于100%的数据 $q\leq 1000, 2 \leq a_i \leq 2^{25}-1$

结论题,直接打表找结论,发现当$a \in [2^{n-1},2^{n}-2] (n \geq 2)$ 答案为$2^{n} - 1$

当$a = 2^n - 1 ,n \geq 1$时答案为a除自己外最大的约数。

直接打表处理26个特殊情况,实际程序复杂度$ O(q) $

# include <bits/stdc++.h>
using namespace std;
int a[27],Pow[27];
int fun(int x)
{
    if (x==0) return 0;
    for (int i=1;i<=26;i++)
         if (x==Pow[i]-1) return a[i];
    int i; 
    for (i=1;i<=26;i++) 
        if (x<=Pow[i]-2) break;
    return  Pow[i]-1;
}
int main()
{
    a[1]=0; a[2]=1; a[3]=1; a[4]=5; a[5]=1;
    a[6]=21; a[7]=1; a[8]=85; a[9]=73; a[10]=341;
    a[11]=89; a[12]=1365; a[13]=1; a[14]=5461;
    a[15]=4681; a[16]=21845; a[17]=1; a[18]=87381;a[19]=1;
    a[20]=349525; a[21]=299593; a[22]=1398101; a[23]=178481;
    a[24]=5592405; a[25]=1082401; a[26]=22369621;
    int n ;scanf("%d",&n);
    Pow[0]=1;
    for (int i=1;i<=26;i++) Pow[i]=Pow[i-1]*2;
    int ans;
    while(n--) {
        int x;scanf("%d",&x);
        printf("%d\n",fun(x));
    }
    return 0;
}
meaning.cpp

Problem 2 Jongmah

给出n个数字在[1,m]内,构成若干个合法三元组,合法的定义是三元组内元素相同或连续。

对于100%的数据,$ n,m\leq 10^6 $

考虑一个dp,先记录每个数出现几次,作为cnt[]数组

考虑若连续三元组超过2个,那么就可以转化为3个相同的三元组,所以在计算中,连续三元组最多为2个。

其他的就被计算在不连续的三元组内。

f[i][k][l]值小于i的所有数,三元组(i,i+1,i+2)选取k$k\in [0,2]$,三元组(i-1,i,i+1)选取l个$l \in [0,2]$ 最多合法三元组个数。

考虑从f[i-1][l][j]转移过来,枚举 j $\in [0,2]$,转移。

当前状态和前继状态比较在原来基础上多出来k个连续三元组(i,i+1,i+2),到此为止,已经用去

数 i-1 消耗 l+j 个; 数 i-2 消耗 j 个 ; 数 i 消耗 k+j+l个 ; 数 i+1 消耗 k+l ;数 i+2 消耗k个。

$ f[i-1][l][j] + k +  \left \lfloor \frac{cnt[i] - k - j - l}{3} \right \rfloor $

初始化全是0

需要满足限制$ cnt[i-1]\geq l+j,cnt[i-2] \geq j,cnt[i] \geq k+j+l,cnt[i+1]\geq k+l,cnt[i+2]\geq k$即可转移。

# include<bits/stdc++.h>
# define fp(i,s,t) for(int i=s;i<=t;i++)
using namespace std;
const int N=1e6+10;
int f[N][4][4],cnt[N];
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    int t;
    fp(i,1,n) scanf("%d",&t),cnt[t]++;
    int ans=0;
    fp(i,1,m) fp(k,0,2) fp(l,0,2) fp(j,0,2)
    {
        if(k+j+l>cnt[i]) continue;
        if(l+j>cnt[i-1]) continue;
        if(j>cnt[i-2]) continue;
        if(k+l>cnt[i+1]) continue;
        if(k>cnt[i+2]) continue; 
        f[i][k][l]=max(f[i][k][l],f[i-1][l][j]+k+(cnt[i]-k-j-l)/3);
        ans=max(ans,f[i][k][l]); 
    } 
    cout<<ans<<'\n';
    return 0;
 }
jongmah.cpp

 Problem C magic

有n个元素数组为$c_i$,对于第$i$个元素($i \in [2,n-1]$)可以发生"同步",即$c_i = c_{i-1} + c_{i+1} - c_i$

给定目标含n个元素数组$t_i$,给出$q个询问,输出$c_i$能否经过若干次"同步"后变成数组$t_i$

我们认为,当两个数组任意对应位置上的数值一样时,这两个数组相同,即$\forall i \in \{1,2...n\} 满足 a_i = b_i 那么 a[] = b[]$

对于100%的数据$n \leq 10^5 , q \leq 10 $

我们对$ c_i = c_{i-1} + c_{i+1} - c_i  $观察对$c$差分数组的影响,

即数组$ \{c_{i-1},c_i,c_{i+1} \}$变成$\{c_{i-1},c_{i-1} + c_{i+1} - c_i,c_{i+1} \}$

对于原来的差分数组,发现差分值从$\{ \delta , c_i-c_{i-1} , c_{i+1}-c_i\}$变成了$\{\delta,c_{i+1}-c_i,c_i - c_{i-1} \}$,

交换差分数组中的两位,若两个数组相等那么其差分数组也相等,但是两个差分数组相等的数组,必须首位相等才会相等。

所以,先判断a[1]=t[1] and a[n]=t[1] 是否成立,若成立 继续判断a[],t[]的差分数组排序后是否相同,若相同输出$ Yes $,若不是此种情况输出 $ No $.

时间复杂度$O(q \times n log_2 n)$

# include <bits/stdc++.h>
# define long long 
using namespace std;
const int N=1e6+10;
int a[N],b[N],da[N],db[N],n;
bool check()
{
    for (int i=1;i<=n;i++)
     if (da[i]!=db[i]) return false;
    return true; 
}
signed main()
{
    int T; scanf("%d",&T);
    while (T--) {
        scanf("%lld",&n);
        for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
        for (int i=1;i<=n;i++) scanf("%lld",&b[i]);
        if (a[1]!=b[1]||a[n]!=b[n]) {
            puts("No");
            continue;
        }
        for (int i=1;i<=n;i++)
         da[i]=a[i]-a[i-1],
         db[i]=b[i]-b[i-1];
        sort(da+1,da+1+n);
        sort(db+1,db+1+n);
        if (check()) puts("Yes");
        else puts("No");    
    }
    
    return 0;
}
magic.cpp

Problem D leaf

一棵根为1的含有n个节点的树,保证每个点i的父亲$p_i<i$,设$(p_i,i)$权值为$w_i$

现在有$m$个询问,每个询问包含$u l r$,描述从节点u出发,到达叶子节点[l,r]中的一个最短路径长度。

数据保证[l,r]节点中一定有叶子节点。

对于100%的数据$n,m \leq 5 \times 10^5$

考虑一个性质,对于这棵树,保证一棵子树中的节点编号是连续的(像树链剖分一样)

然后就可以维护一棵线段树,离线算法统计了。

首先从根出发,到各叶子节点,记录dis[]数组表示唯一路径长度。

对于叶子节点,将其在线段树中的值+dis[v],然后对该树从根1开始进行dfs,

线段树中保存的是各个叶子节点的权值(不是叶子节点的值为正无穷)

考虑从u走到v经过一条权为w的边那么考虑对该子树中的叶子路径是减小w,但是不在该子树中的叶子路径时增大w。

而这棵树又有一个子树节点编号连续的性质,所以先全局[1,n] + w , 然后 直接线段树区间修改 [u,mx[u]] -2w ,其中mx[u]表示以节点u为子树根的最大编号的儿子。

离线统计答案。

注意最大值inf不要开得太满,否则会爆long long。

# pragma GCC optimize(2)
# include <cstdio>
# include <cstring>
# include <vector>
# define int long long
using namespace std;
const int N=1e6+10;
struct rec{int pre,to,w;}a[N];
struct node{int l,r,val,tag;};
const int inf=5e14;
int n,m,tot;
int mx[N],l[N],r[N],head[N],ans[N],dis[N]; 
vector<int>q[N];
inline int read()
{
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
inline void write(int x)
{
    if (x<0) x=-x,putchar('-');
    if (x>9) write(x/10);
    putchar('0'+x%10);
}
struct Segment_Tree{
    node t[N<<2];
    # define ls 2*x
    # define rs 2*x+1
    void build (int x,int l,int r)
    {
       t[x].l=l; t[x].r=r;
       if (l==r) { t[x].val=0; return;}
       int mid=(l+r)>>1;
       build(ls,l,mid);
       build(rs,mid+1,r);
       t[x].val=max(t[ls].val,t[rs].val);
    }
    void down(int x)
    {
       t[ls].val+=t[x].tag; t[rs].val+=t[x].tag;
       t[ls].tag+=t[x].tag; t[rs].tag+=t[x].tag;
       t[x].tag=0;
    }
    void update(int x,int opl,int opr,int d)
    {
       if (opl<=t[x].l&&t[x].r<=opr) {
            t[x].val+=d;t[x].tag+=d;
            return;
       }
       down(x);
       int mid=(t[x].l+t[x].r)/2; 
       if (opl<=mid) update(ls,opl,opr,d);
       if (opr>mid) update(rs,opl,opr,d);
       t[x].val=min(t[ls].val,t[rs].val);
    }
    int query(int x,int opl,int opr)
    {
        if (opl<=t[x].l&&t[x].r<=opr) return t[x].val;
        down(x);
        int mid=(t[x].l+t[x].r)/2;
        int val=inf;
        if (opl<=mid) val=min(val,query(ls,opl,opr));
        if (opr>mid) val=min(val,query(rs,opl,opr));
        return val;
    }
}tree;
void adde(int u,int v,int w)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    a[tot].w=w;
    head[u]=tot;
}
void dfs1(int u)
{
    mx[u]=u;
    for (int i=head[u];i;i=a[i].pre){
        int v=a[i].to;
        dis[v]=dis[u]+a[i].w;
        dfs1(v);
        mx[u]=max(mx[u],mx[v]);
    }
}
void dfs2(int u)
{
    for (int i=0;i<q[u].size();i++) {
        ans[q[u][i]]=tree.query(1,l[q[u][i]],r[q[u][i]]);
    }
    for (int i=head[u];i;i=a[i].pre){
        int v=a[i].to,w=a[i].w;
        tree.update(1,1,n,w); tree.update(1,v,mx[v],-2*w);
        dfs2(v);
        tree.update(1,1,n,-w); tree.update(1,v,mx[v],2*w);
    }
}
signed main()
{
    n=read();m=read();
    tree.build(1,1,n);
    for (int i=2;i<=n;i++) {
        int u=read(),v=i,w=read();
        adde(u,v,w);
    }
    dfs1(1);
    for (int i=1;i<=n;i++) 
      if (i==mx[i]) tree.update(1,i,i,dis[i]);
      else tree.update(1,i,i,inf);
    for (int i=1;i<=m;i++) {
        int v=read(); l[i]=read();r[i]=read();
        q[v].push_back(i);
    }
    dfs2(1);
    for (int i=1;i<=m;i++) write(ans[i]),putchar('\n');
    return 0;
}
leaf.cpp

 

posted @ 2019-03-10 17:12  ljc20020730  阅读(180)  评论(0编辑  收藏  举报