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; }
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; }
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; }
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; }