E
有一堆(i,ai)(i,a_i)(i,ai)这样的有向边,问图上有多少对(u,v)(u,v)(u,v),存在路径u−vu-vu−v。
首先这个边的形式,意味着这是个内向基环树,所有点都能到达一个环上。缩点之后,就是计算新图上每个点,有多少个点能到达这个点。这可以拓扑排序dpdpdp。然后每个点的贡献就是szi∗dpisz_i*dp_iszi∗dpi,dpdpdp结果乘上新图上这个点在旧图上的点数,这是为了处理环有多个点的情况,环上点到达一个就能到达其他
c
int in[N],dfn[N],co[N],low[N],s[N],ind[N],top,cnt,tot;
vector<int>a[N],b[N];
inline void tarjan(int x){
dfn[x]=low[x]=++cnt;
s[++top]=x;
ind[x]=1;
for(int v:a[x]){
if(!dfn[v]){
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if(ind[v]){
low[x]=min(low[x],low[v]);
}
}
if(low[x]==dfn[x]){
++tot;
while(1){
int X=s[top--];
co[X]=tot;
ind[X]=0;
if(!(x^X)){
break;
}
}
}
}
int sz[N],w[N];
void solve()
{
int n;
cin>>n;
rep(i,1,n){
int x;
cin>>x;
a[x].push_back(i);
}
rep(i,1,n){
if(!dfn[i]){
tarjan(i);
}
}
rep(i,1,n){
sz[co[i]]++;
w[co[i]]++;
for(int v:a[i]){
if(co[i]^co[v]){
b[co[i]].push_back(co[v]);
in[co[v]]++;
}
}
}
queue<int>q;
rep(i,1,tot){
if(!in[i]){
q.push(i);
}
}
while(q.size()){
int u=q.front();
q.pop();
for(int v:b[u]){
w[v]+=w[u];
if(--in[v]==0){
q.push(v);
}
}
}
int ans=0;
rep(i,1,tot){
ans+=sz[i]*w[i];
}
cout<<ans;
}
F
懒标记线段树,两个序列区间加,每次询问∑ai∗bi\sum a_i*b_i∑ai∗bi
一个线段树可以解决,维护a,ba,ba,b两个序列的懒标记,区间和,维护a∗ba*ba∗b的区间和。aaa的懒标记下放时,会对aaa的懒标记区间和产生影响,也会对a∗ba*ba∗b区间和产生影响,bbb的懒标记下放同理。修改需要用两个函数或者参数来表示加的是a,ba,ba,b哪个序列。
c
struct Tree
{
#define ls u << 1
#define rs u << 1 | 1
struct Node
{
int l, r;
ll sa,sb,sum, adda,addb;
} tr[N << 2];
void pushup(int u)
{
tr[u].sa = tr[ls].sa + tr[rs].sa;
tr[u].sb = tr[ls].sb + tr[rs].sb;
tr[u].sum = tr[ls].sum + tr[rs].sum;
tr[u].sa%=M2;
tr[u].sb%=M2;
tr[u].sum%=M2;
}
void pushdown(int u)
{
if (tr[u].adda)
{
tr[ls].sa += tr[u].adda * (tr[ls].r - tr[ls].l + 1)%M2;
tr[rs].sa += tr[u].adda * (tr[rs].r - tr[rs].l + 1)%M2;
tr[ls].adda += tr[u].adda;
tr[rs].adda += tr[u].adda;
tr[ls].sum+=tr[u].adda*tr[ls].sb%M2;
tr[rs].sum+=tr[u].adda*tr[rs].sb%M2;
tr[ls].sa%=M2;tr[ls].adda%=M2;tr[ls].sum%=M2;
tr[rs].sa%=M2;tr[rs].adda%=M2;tr[rs].sum%=M2;
tr[u].adda = 0;
}
if (tr[u].addb)
{
tr[ls].sb += tr[u].addb * (tr[ls].r - tr[ls].l + 1)%M2;
tr[rs].sb += tr[u].addb * (tr[rs].r - tr[rs].l + 1)%M2;
tr[ls].addb += tr[u].addb;
tr[rs].addb += tr[u].addb;
tr[ls].sum+=tr[u].addb*tr[ls].sa%M2;
tr[rs].sum+=tr[u].addb*tr[rs].sa%M2;
tr[ls].sb%=M2;tr[ls].addb%=M2;tr[ls].sum%=M2;
tr[rs].sb%=M2;tr[rs].addb%=M2;tr[rs].sum%=M2;
tr[u].addb = 0;
}
}
void build(int u, int l, int r)
{
tr[u] = {l, r, 0, 0,0,0,0};
if (l == r)
{
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int val,int op)
{
if (tr[u].l >= l && tr[u].r <= r)
{
if(op==1){
tr[u].sa += val * (tr[u].r - tr[u].l + 1)%M2;
tr[u].sum+=val*tr[u].sb%M2;
tr[u].adda += val;
tr[u].sa%=M2;
tr[u].sum%=M2;
tr[u].adda%=M2;
}
else{
tr[u].sb += val * (tr[u].r - tr[u].l + 1)%M2;
tr[u].sum+=val*tr[u].sa%M2;
tr[u].addb += val;
tr[u].sb%=M2;
tr[u].sum%=M2;
tr[u].addb%=M2;
}
return;
}
else
{
int mid = (tr[u].l + tr[u].r) >> 1;
pushdown(u);
if (mid >= l)
modify(ls, l, r, val,op);
if (r > mid)
modify(rs, l, r, val,op);
pushup(u);
}
}
ll query(int u, int l, int r)
{
if (l <= tr[u].l && tr[u].r <= r)
return tr[u].sum;
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (r <= mid)
return query(ls, l, r);
if (l > mid)
return query(rs, l, r);
return (query(ls, l, r) + query(rs, l, r))%M2;
}
} t;
void solve()
{
int n,q;
cin>>n>>q;
t.build(1,1,n);
rep(i,1,n){
int x;
cin>>x;
t.modify(1,i,i,x,1);
}
rep(i,1,n){
int x;
cin>>x;
t.modify(1,i,i,x,2);
}
rep(i,1,q){
int op,l,r;
cin>>op>>l>>r;
if(op==1||op==2){
int x;
cin>>x;
t.modify(1,l,r,x,op);
}
else{
cout<<t.query(1,l,r)<<'\n';
}
}
}