这一场并没有考,
然后题目还是补掉了的。
T1 special tree
题目大意
一棵单向向下的树,另外每个点有一条到根节点的边。
两种操作:修改某边权以及回答两点间最短距离。
分析
因为这棵树长得十分奇怪,
所以实际上走的路径种数很少。
只有两种方案:
直接从u向下走到v,
从u子树中某个点走到根然后再走到v。
所以只用维护深度和子树内到根的距离。
DFS序求出子树区间。
然后我们用一个普通的延迟更新线段树就能同时弄掉这两个东西。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
void Rd(int &res){
static char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define M 200004
#define LL long long
#define lp (p<<1)
#define rp (p<<1|1)
#define lson l,mid,lp
#define rson mid+1,r,rp
#define INF 0x3f3f3f3f3f3f3f3f
#define chkmin(a,b) a=min(a,b)
struct SegTree{
LL Mn[M<<2],Dn[M<<2];
void Down(int p){
if(Dn[p]){
Mn[lp]+=Dn[p];
Mn[rp]+=Dn[p];
Dn[lp]+=Dn[p];
Dn[rp]+=Dn[p];
Dn[p]=0;
}
}
void Updata(int l,int r,int p,int a,int b,LL t){
if(l>b || r<a)return;
if(a<=l&&r<=b){Mn[p]+=t,Dn[p]+=t;return;}
Down(p);
int mid=l+r>>1;
Updata(lson,a,b,t);
Updata(rson,a,b,t);
Mn[p]=min(Mn[lp],Mn[rp]);
}
LL Query(int l,int r,int p,int a,int b){
if(l>b || r<a)return INF;
if(a<=l&&r<=b)return Mn[p];
Down(p);
int mid=l+r>>1;
return min(Query(lson,a,b),Query(rson,a,b));
}
}Tree;
int Next[M],V[M],Head[M],ltot;
void Add_Edge(int a,int b){
Next[++ltot]=Head[a],V[Head[a]=ltot]=b;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
int n,q,ReA[M],ReC[M],Re[M];
int A[M],B[M],C[M];
int DFN[M],End[M],dfn;
void DFS(int A){
int B;
DFN[A]=++dfn;
LREP(i,A)
DFS(B=V[i]);
End[A]=dfn;
}
int main(){
Rd(n),Rd(q);
REP(i,1,n){
Rd(A[i]),Rd(B[i]),Rd(C[i]);
Add_Edge(A[i],B[i]);
}
REP(i,1,n){
Rd(ReA[i]),Rd(ReC[i]),Rd(ReC[i]);
Re[ReA[i]]=ReC[i];
}
DFS(1);
REP(i,1,n) Tree.Updata(1,n,1,DFN[B[i]],End[B[i]],C[i]);
REP(i,1,n) Tree.Updata(1,n,1,DFN[ReA[i]],DFN[ReA[i]],ReC[i]);
while(q--){
int a,b,c;
Rd(a),Rd(b),Rd(c);
if(a==1){
if(b>=n){
b-=n-1;
Tree.Updata(1,n,1,DFN[ReA[b]],DFN[ReA[b]],c-ReC[b]);
ReC[b]=Re[ReA[b]]=c;
}
else{
Tree.Updata(1,n,1,DFN[B[b]],End[B[b]],c-C[b]);
C[b]=c;
}
}
else {
LL D1=Tree.Query(1,n,1,DFN[b],DFN[b])-Re[b];
LL D2=Tree.Query(1,n,1,DFN[c],DFN[c])-Re[c];
LL Val=Tree.Query(1,n,1,DFN[b],End[b]);
LL Ans=Val-D1+D2;
if(DFN[b]<=DFN[c] && DFN[c]<=End[b])chkmin(Ans,D2-D1);
printf("%lld\n",Ans);
}
}
return 0;
}
T2 div tree
分析
P40
这个40分只需要暴力DP,
然后直接累计一下答案。
P70
此时只要求一个答案。
所以可以把这个数分解质因数然后用贪心来求。
P100
沿用P70的方法,
加一个筛出质因数即可,
因为质因数不会很多所以复杂度不是很大。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define LLREP(i,a,b) for(LL i=(a),i##_end_=(b);i<i##_end_;i++)
#define LL long long
#define chkmax(a,b) a=max(a,b)
LL A,B;
struct P40{
static const int M=1000004;
int DP[M],Deg[M];
void Solve(){
DP[1]=1;
REP(i,2,M)Deg[i]=1;
REP(i,1,B+1)
for(int j=i+i;j<=B;j+=i)
Deg[j]++,chkmax(DP[j],DP[i]+Deg[i]);
LL Ans=0;
REP(i,A,B+1)
Ans+=DP[i]+Deg[i]-1;
printf("%lld\n",Ans);
}
}P40;
struct P100{
static const int M=1000004;
bool Mark[M];
LL Num[M];
vector<int>Deg[M];
#define pb push_back
#define SZ(a) ((int)(a).size())
LL Answer(vector<int>D){
int Len=SZ(D),v;
LL Res=0,Sum;
while(1){
Sum=D[v=0]+1;
REP(i,1,Len){
Sum*=D[i]+1;
if(D[i]>D[v])v=i;
}
if(!D[v])break;
D[v]--;
Res+=Sum;
}
return Res;
}
void Solve(){
REP(i,0,B-A+1)Num[i]=A+i;
REP(i,2,M){
if(!Mark[i]){
for(int j=i+i;j<M;j+=i)Mark[j]=1;
LL Pos=B/i*i-A;
while(Pos>=0){
int Res=0;
while(!(Num[Pos]%i))
Res++,Num[Pos]/=i;
Deg[Pos].pb(Res);
Pos-=i;
}
}
}
REP(i,0,B-A+1)if(Num[i]!=1)Deg[i].pb(1);
LL Ans=0;
REP(i,0,B-A+1)
Ans+=Answer(Deg[i]);
printf("%lld\n",Ans);
}
}P100;
int main(){
scanf("%lld %lld",&A,&B);
if(B<=1000000)P40.Solve();
else P100.Solve();
return 0;
}
T3 skt
暂略