T1
题面

解题
- 注意到操作 1 与操作 3 均与某节点的整棵子树有关,遂利用 dfs 序将树上问题转化为区间问题,区间问题常用线段树解决。
- 考虑如何实现操作 1。易得,\((x+k*(depth_u-depth_v))\times(-1)^d=(x+k*(depth_u-depth_v))\times(-1)^{depth_u+depth_v}=(-1)^{depth_u}\times\{(-1)^{depth_v}\times[x-k*depth_v+k*depth_u]\}\)。故,可先对某个区间维护 \((-1)^{depth_v}\times(x-k*depth_v)\) 以及 \((-1)^{depth_v}\times k*depth_u\) 中的系数 \((-1)^{depth_v}\times k\)。\((-1)^{depth_u}\) 可等到操作 2 查询某个节点时再乘上。
- 由于操作 1 的次数不超过 \(m\) 次,故枚举 \(v\) 子树中的每个节点,跳过没有操作 1 (未有过,或先前已删除且未补充)的节点,即可保证时间复杂度。
- 时间复杂度为 \(\mathcal O(m\log n)\)。
代码
点击查看代码
#include<bits/stdc++.h>
#define lson i<<1
#define rson i<<1|1
#define int long long
using namespace std;
const int maxn=2e5+100;
const int MOD=1e9+7;
int tg[maxn<<2],tgi[maxn<<2];
int head[maxn],cnt;
struct edge
{
int v,next;
}e[maxn<<1];
void add(int u,int v)
{
e[++cnt]=(edge){v,head[u]};
head[u]=cnt;
}
int n,m,dfn[maxn],cnti,sz[maxn],depth[maxn];
int invdfn[maxn];
void dfs(int u,int d)
{
depth[u]=d;
dfn[u]=++cnti,sz[u]=1;
invdfn[cnti]=u;
int v;
for(int i=head[u];i;i=e[i].next)
v=e[i].v,dfs(v,d+1),sz[u]+=sz[v];
}
void pushdown(int i)
{
if(tg[i]!=0||tgi[i]!=0)
{
//cout<<i<<" "<<tg[i]<<" "<<tgi[i]<<" here"<<endl;
tg[lson]=((tg[lson]+tg[i])%MOD+MOD)%MOD;
tgi[lson]=((tgi[lson]+tgi[i])%MOD+MOD)%MOD;
tg[rson]=((tg[rson]+tg[i])%MOD+MOD)%MOD;
tgi[rson]=((tgi[rson]+tgi[i])%MOD+MOD)%MOD;
tg[i]=0,tgi[i]=0;
}
return;
}
void merge(int i,int cl,int cr,int ql,int qr,int k,int ki)
{
if(cl>qr||ql>cr) return;
if(ql<=cl&&qr>=cr)
{
tg[i]=((tg[i]+k)%MOD+MOD)%MOD;
tgi[i]=((tgi[i]+ki)%MOD+MOD)%MOD;
//cout<<i<<" "<<tg[i]<<" "<<tgi[i]<<" here\n";
return;
}
pushdown(i);
int mid=(cl+cr)>>1;
merge(lson,cl,mid,ql,qr,k,ki),
merge(rson,mid+1,cr,ql,qr,k,ki);
}
int h[maxn],hi[maxn];
int query(int i,int cl,int cr,int pos,int d)
{
if(pos==cl&&pos==cr)
return ((tg[i]+1ll*tgi[i]*d%MOD)%MOD+MOD)%MOD;
int mid=(cl+cr)>>1;
pushdown(i);
if(pos<=mid) return query(lson,cl,mid,pos,d);
else return query(rson,mid+1,cr,pos,d);
}
signed main()
{
scanf("%lld%lld",&n,&m);
int u;
for(int i=1;i<n;i++)
scanf("%lld",&u),add(u,i+1);
dfs(1,1);
int v,x,k,op;
while(m--)
{
scanf("%lld",&op);
if(op==1)
{
scanf("%lld%lld%lld",&v,&x,&k);
int inv=(depth[v]%2==1?-1:1);
//cout<<v<<" "<<depth[v]<<endl;
int ki=((-1ll*k*depth[v]%MOD+x%MOD)%MOD+MOD)%MOD;
int kii=(k%MOD+MOD)%MOD;
//cout<<ki<<" "<<kii<<endl;
merge(1,1,n,dfn[v],dfn[v]+sz[v]-1,(ki*inv%MOD+MOD)%MOD,(kii*inv%MOD+MOD)%MOD);
h[v]=((h[v]+inv*ki)%MOD+MOD)%MOD;
hi[v]=((hi[v]+inv*kii)%MOD+MOD)%MOD;
}
if(op==2)
{
scanf("%lld",&v);
int inv=(depth[v]%2==1?-1:1);
printf("%lld\n",(inv*query(1,1,n,dfn[v],depth[v])%MOD+MOD)%MOD);
}
if(op==3)
{
int u;
scanf("%lld",&u);
//cout<<v<<" "<<h[v]<<" "<<hi[v]<<endl;
for(int vi=dfn[u];vi<=dfn[u]+sz[u]-1;vi++)
{
int v=invdfn[vi];
if(h[v]!=0||hi[v]!=0)
merge(1,1,n,dfn[v],dfn[v]+sz[v]-1,(-h[v]+MOD)%MOD,(-hi[v]+MOD)%MOD),
h[v]=0,hi[v]=0;
}
}
}
return 0;
}
/*
5 11
1
1
3
3
1 1 0 2
2 1
2 2
3 3
2 4
3 1
1 3 1 3
2 3
2 4
3 1
2 1
*/
T2
题面

解题
- 结论:按照连点顺序得到的图形满足要求等价于所有点全部属于凸包,而且由 Andrew 算法得到的序列以顺时针(或逆时针方向)与连点顺序完全相同,且所有点不在同一条直线上。
- oiwiki-凸包。
代码(待评测)
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+100;
int n;
struct point
{
ll x,y;
bool operator <(point a)const
{
return a.x<x;
}
ll operator * (point b)
{
return x*b.y-y*b.x;
}
ll operator /(point b)
{
return x*b.x+y*b.y;
}
point operator -(point b)
{
return (point){x-b.x,y-b.y};
}
}p[maxn];
map<pair<int,int>,bool> mp;
int h[maxn],st[maxn],tp,used[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&p[i].x,&p[i].y);
if(mp.find({p[i].x,p[i].y})!=mp.end())
{
printf("No");return 0;
}
mp[{p[i].x,p[i].y}]=true;
}
sort(p+1,p+n+1);
st[++tp]=1;//used[1]=1;
for(int i=2;i<=n;i++)
{
while(tp>=2&&
(p[st[tp]]-p[st[tp-1]])*(p[i]-p[st[tp]])<0)
used[st[tp--]]=0;
used[i]=1,st[++tp]=i;
}
int tmp=tp;
for(int i=n-1;i>=1;i--)
{
while(tp>tmp&&
(p[st[tp]]-p[st[tp-1]])*(p[i]-p[st[tp]])<0)
used[st[tp--]]=0;
used[i]=1,st[++tp]=i;
}
for(int i=1;i<=n;i++)
if(!used[i]) {printf("No");return 0;}
int idx=1;
while(st[idx]!=1) idx++;
for(int i=idx;i<tp+idx-1;i++)
h[i-idx+1]=st[(i<tp?i:i-tp+1)];
for(int i=1;i<=n;i++)
{
int last=(i-1?i-1:n);
int nxt=(i+1<=n?i+1:1);
if((p[i]-p[last])*(p[nxt]-p[i])==0&&
(p[i]-p[last])/(p[nxt]-p[i])<0)
{printf("No");return 0;}
}
for(int i=1;i<tp;i++)
{
if(i!=h[i]) break;
if(i==tp-1) {printf("Yes");return 0;}
}
h[tp]=1;
for(int i=tp,k=1;i>1;i--,k++)
{
if(k!=h[i]) break;
if(k==2) {printf("Yes");return 0;}
}
printf("No");
return 0;
}
/*
3
0 0
1 0
0 1
4
0 0
0 1
1 1
1 0
4
0 0
0 3
1 2
1 1
3
0 0
0 0
0 0
*/