CF1485X Codeforces Round #701
D Multiples and Power Differences (构造)
题目大意:给一个n*m的矩阵a,a[i][j]在1到16之间。现在要构造矩阵b,需要满足如下条件:
1.b[i][j]在1到1e6之间
2.b[i][j]是a[i][j]的倍数
3.对于矩阵中任意相邻的两个数,要求存在正整数k,他们的差值绝对值等于k的四次方,不要求所有对的k都相同
构造神仙题,乍一看不可做。。
考虑第二个条件,由于a最多到16,发现1到16所有数的最小公倍数是$720720$!小于1e6。
在考虑第三个条件,可以对b矩阵黑白染色,黑点构造成$720720+a[i][j]^{4}$,白点是$720720$
矩阵问题不仅可以建边跑图论,还可以染色处理
1 const int N1=505; const int maxn=720720; 2 3 int n,m; 4 int a[N1][N1],b[N1][N1]; 5 6 int main() 7 { 8 freopen("a.in","r",stdin); 9 scanf("%d%d",&n,&m); 10 for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]); 11 for(int i=1;i<=n;i++,puts("")) for(int j=1;j<=m;j++) 12 { 13 if((i+j)%2==0){ 14 b[i][j]=maxn; 15 }else{ 16 b[i][j]=maxn+a[i][j]*a[i][j]*a[i][j]*a[i][j]; 17 } 18 printf("%d ",b[i][j]); 19 } 20 return 0; 21 }
E Move and Swap (树形DP)
题目大意:给你一棵有根树,开始时有红蓝两个棋子放在根上,一次移动需要进行如下操作:
设红蓝棋子当前未知分别为r,b
1.把红棋子移动到r的某个子节点上
2.把蓝棋子移动到深度=dep[b]+1的某个点上
3.可选择是否交换红蓝棋子的位置
每次移动产生$|a[r]-a[b]|$点代价,现在求代价之和的最大值
对点按深度从大到小依次处理
令$f[i]$表示$i$节点放红色棋子时的最大贡献,$g[j]$表示$j$子节点中$f$的最大值
可得转移方程$f[i]=max\left \{ max(g[i],g[j])+|a[i]-a[j]| \right \} $
将相同深度的点按a排序,正反各计算一次,即可消掉绝对值,再用前缀/后缀最大值优化转移即可
1 const int N1=200005; const ll inf=0x3f3f3f3f3f3f3f3fll; 2 3 int n,T; 4 struct EDGE{ 5 int to[N1],nxt[N1],head[N1],cte; 6 void ae(int u,int v) 7 { cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte; } 8 }e; 9 10 ll f[N1],g[N1]; 11 int fa[N1],a[N1],dep[N1],madep; 12 vector<int>id[N1]; 13 int cmp1(int x,int y){ return a[x]<a[y]; } 14 void clr() 15 { 16 for(int i=1;i<=n;i++) e.head[i]=0, id[i].clear(), f[i]=g[i]=dep[i]=0; 17 while(e.cte) e.to[e.cte]=e.nxt[e.cte]=0, e.cte--; 18 } 19 20 void dfs(int u) 21 { 22 for(int j=e.head[u];j;j=e.nxt[j]) 23 { 24 int v=e.to[j]; dep[v]=dep[u]+1; madep=max(madep,dep[v]); 25 id[dep[v]].push_back(v); 26 dfs(v); 27 } 28 } 29 30 int main() 31 { 32 freopen("a.in","r",stdin); 33 scanf("%d",&T); 34 while(T--){ 35 36 scanf("%d",&n); 37 for(int i=2;i<=n;i++) scanf("%d",&fa[i]), e.ae(fa[i],i); 38 for(int i=2;i<=n;i++) scanf("%d",&a[i]); 39 madep=0; dfs(1); 40 ll tma,ma; 41 for(int k=madep,i;k>=1;k--) 42 { 43 sort(id[k].begin(),id[k].end(),cmp1); 44 tma=-inf; ma=-inf; 45 for(int j=0;j) 46 { 47 i=id[k][j]; tma=max(tma,g[i]-a[i]); ma=max(ma,-1ll*a[i]); 48 f[i]=max(tma+a[i],g[i]+a[i]+ma); 49 } 50 tma=-inf; ma=-inf; 51 for(int j=(int)id[k].size()-1;j>=0;j--) 52 { 53 i=id[k][j]; tma=max(tma,g[i]+a[i]); ma=max(ma,1ll*a[i]); 54 f[i]=max(f[i],max(tma-a[i],g[i]+ma-a[i])); 55 } 56 for(int j=0;j ) 57 { 58 i=id[k][j]; g[fa[i]]=max(g[fa[i]],f[i]); 59 } 60 } 61 printf("%lld\n",g[1]); 62 clr(); 63 64 } 65 return 0; 66 }
F Copy or Prefix Sum (线段树优化DP)
题目大意:给定数组b,现在求有多少满足如下条件的a数组:
$b[i]=a[i]$或$b[i]=\sum_{j=1}^{i}a[j]$
考场上最后一分钟调出来了,结果我非得给数组多按个0然后CE(MLE)了。。不然就不会掉分了
发现第二个条件是前缀和比较特殊,并且第一个条件不影响答案
考虑DP选择第二个条件的位置
定义f[i]表示i位置满足第二个条件且不满足第一个条件时的方案数
不满足第一个条件时$b[i]\ne a[i]$,计算可得$sum[i-1]-sum[j-1]\ne 0$
$f[i]=\sum_{j=1}^{i-1}f[j](sum[i-1]\ne sum[j-1])$
线段树优化DP就好了!又由于sum值域较大且可能为负,动态开点+整体往右挪
1 const int N1=200005; const int inf=0x3f3f3f3f; 2 const ll maxn=1e15; 3 const int p=1000000007; 4 5 int T,n; 6 int a[N1],b[N1]; 7 ll f[N1]; 8 struct SEG{ 9 ll sum[N1*65]; int ls[N1*65],rs[N1*65],tot; 10 void pushup(int rt){ sum[rt]=(sum[ls[rt]]+sum[rs[rt]])%p; } 11 void upd(ll x,ll l,ll r,int &rt,ll w) 12 { 13 if(!rt) rt=++tot; 14 if(l==r){ (sum[rt]+=w)%=p; return; } 15 ll mid=(l+r)>>1; 16 if(x<=mid) upd(x,l,mid,ls[rt],w); 17 else upd(x,mid+1,r,rs[rt],w); 18 pushup(rt); 19 } 20 ll query(ll L,ll R,ll l,ll r,int rt) 21 { 22 if(!rt) return 0; 23 if(L<=l&&r<=R) return sum[rt]; 24 ll mid=(l+r)>>1; ll ans=0; 25 if(L<=mid) ans+=query(L,R,l,mid,ls[rt]); 26 if(R>mid) ans+=query(L,R,mid+1,r,rs[rt]); 27 ans%=p; 28 return ans; 29 } 30 void clr() 31 { 32 while(tot) sum[tot]=0, ls[tot]=rs[tot]=0, tot--; 33 } 34 }s; 35 ll sum[N1]; 36 37 int main() 38 { 39 scanf("%d",&T); 40 ll ans=0,mi=maxn,ma=-maxn; 41 while(T--) 42 { 43 scanf("%d",&n); 44 for(int i=1;i<=n;i++) scanf("%d",&b[i]), sum[i]=sum[i-1]+b[i], mi=min(mi,sum[i]), ma=max(ma,sum[i]); 45 ll ans=1; f[1]=0; int root=0; 46 for(int i=2;i<=n;i++) 47 { 48 // f[i]=1; 49 if(sum[i-1]!=0) f[i]=1; else f[i]=0; 50 // for(int j=1;j51 // (f[i]+=f[j])%=p; 52 if(mi1 ]) (f[i]+=s.query(mi+maxn,sum[i-1]+maxn-1,mi+maxn,ma+maxn,root)); 53 if(sum[i-1]1]+maxn+1,ma+maxn,mi+maxn,ma+maxn,root)); 54 f[i]%=p; 55 s.upd(sum[i-1]+maxn,mi+maxn,ma+maxn,root,1ll*f[i]); 56 (ans+=f[i])%=p; 57 } 58 printf("%lld\n",ans); 59 s.clr(); 60 } 61 return 0; 62 }