T1 与或和

 

2s&&512MB

 

简明题意:求一个矩阵的所有子序列的 \(and\)和 和\(or\)和;

子矩阵的\(and\)和就是所有值\(and\)起来;\(or\)类似;

矩阵边长\(n<=1000\),权值\(<=2^{31}-1\)

 

\(\&\)和\(\ |\)运算没有逆运算,所以无法算前缀和;但这两种运算中 二进制下的每一位是独立运算的,我们考虑将每个数看成\(30\)位\(01\)串,一位一位分开算;

先看与运算,枚举每一位\(i\),只有当一个子矩阵这一位全是\(1\)时,这个矩阵会在这一位上对答案产生贡献;

所以我们就是要找到全\(1\)子矩阵个数,对答案\(ans1\)产生个数\(*(1<<i)\)的贡献;接下来就是每行一个单调栈的过程;

或运算相反,需要求全\(0\)矩阵,但每次贡献应该减去,初始\(ans2\)应该是所有子矩阵个数\(*((1<<30)-1)\)

 

 

 

\(Code\)

#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
using namespace std;
inline int read()
{
int x=0,fl=1;char st=getchar();
while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
return x*fl;
}
const int N=1005;
const int mod=1e9+7;
int n,a[N][N],h[N],f[N],h1[N];
int sta[N],top;
ll tmp;
ll ans1,ans2;
int main()
{
freopen("andorsum.in","r",stdin);
freopen("andorsum.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
a[i][j]=read();
ans2=(ans2+i*j)%mod;
} ans2=ans2*(((1ll<<31)-1)%mod)%mod;
for(int o=0;o<=30;o++)
{
tmp=0;
for(int i=1;i<=n;i++) h[i]=0,f[i]=0,h1[i]=0;
for(int i=1;i<=n;i++)
{
top=0;
for(int j=1;j<=n;j++)
{
if((a[i][j]>>o)&1) h[j]++;
else h[j]=0;
while(h[sta[top]]>=h[j]&&top) top--;
f[j]=h[j]*(j-sta[top]);
f[j]+=f[sta[top]];
tmp+=f[j];
sta[++top]=j;
}
}
ans1+=tmp%mod*(1ll<<o)%mod;ans1%=mod;
tmp=0;
for(int i=1;i<=n;i++)
{
top=0;
for(int j=1;j<=n;j++)
{
if((a[i][j]>>o)&1) h1[j]=0;
else h1[j]++;
while(h1[sta[top]]>=h1[j]&&top) top--;
f[j]=h1[j]*(j-sta[top]);
f[j]+=f[sta[top]];
tmp+=f[j];
sta[++top]=j;
}
}
ans2-=tmp%mod*(1ll<<o)%mod;ans2=(ans2+mod)%mod;
}
printf("%lld %lld",ans1,ans2);
fclose(stdin);
fclose(stdout);
return 0;
}

 

 

 

T2 宝牌一大堆

 

链接

先明确刚子永远没有选刻子优秀,就不考虑他了;

现在只有\(3\)种情况,国士无双和七对子可以单独模拟,我们只需要求\(3*4+2\)这种情况了;

需要进行\(DP\);

定义\(f[i][j][k][l][m][n]\)代表,前\(i\)种牌,选了\(j\)组面子,\(k\)组雀头,第\(i-2\)种用了\(l\),第 \(i-1\)种用了\(m\)张,第\(i\)种用了\(n\)张,前\(i-1\)种牌产生的分数;

 

 

然后再转移是选一组刻子顺子或雀头;

 

 

很恶毒的一个\(DP\)

#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define Vector point
using namespace std;
inline int read()
{
int x=0,fl=1;char st=getchar();
while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
return x*fl;
}
int T;
int v[128],num[35],vab[35],c[10][10],val[35],V[35];
ll ans;
char st[5];
inline void reset()
{
for(int i=1;i<=34;i++) num[i]=4,V[i]=1;
memset(vab,0,sizeof vab);
}
inline ll ksm(int x,int y)
{
ll res=1;
ll X=x;
while(y){ if(y&1) res=res*X;X*=X;y>>=1;}
return res;
}
inline ll gsws()
{
int sum=0,S=0;
ll res=1;
for(int i=0;i<3;i++)
{
if(num[1+i*9]==0) return 0;
else sum+=num[1+i*9],S+=vab[1+i*9],res=res*c[num[1+i*9]][1];
if(num[9+i*9]==0) return 0;
else sum+=num[9+i*9],S+=vab[9+i*9],res=res*c[num[9+i*9]][1];
}
for(int i=28;i<=34;i++)
if(num[i]==0) return 0;
else sum+=num[i],S+=vab[i],res=res*c[num[i]][1];
if(sum<14) return 0;
ll tmp=res;
res=0;
for(int i=0;i<3;i++)
{
if(num[1+i*9]>=2) res=max(res,tmp/c[num[1+i*9]][1]*c[num[1+i*9]][2]*ksm(2,S+vab[1+9*i]));
if(num[9+i*9]>=2) res=max(res,tmp/c[num[9+i*9]][1]*c[num[9+i*9]][2]*ksm(2,S+vab[9+9*i]));
}
for(int i=28;i<=34;i++)
if(num[i]>=2) res=max(res,tmp/c[num[i]][1]*c[num[i]][2]*ksm(2,S+vab[i]));
return res*13;
}
inline ll get7()
{
int num7=0;
for(int i=1;i<=34;i++)
{
if(num[i]>=2) num7++,val[i]=c[num[i]][2]*(vab[i]?4:1);
else val[i]=1;
}
if(num7<7) return 0;
ll res=1;
sort(val+1,val+1+34);
for(int i=34;i>=28;i--)
res*=val[i];
return res*7;
}
ll f[40][5][2][5][5][5],tmp;
int main()
{
freopen("doraippai.in","r",stdin);
freopen("doraippai.out","w",stdout);
c[0][0]=1;c[1][0]=1;c[2][0]=1;c[3][0]=1;c[4][0]=1;c[1][1]=1;c[2][1]=2;c[2][2]=1;c[3][1]=3;c[3][2]=3;c[3][3]=1;c[4][1]=4;c[4][2]=6;c[4][3]=4;c[4][4]=1;
v['p']=1;
v['s']=2;
v['E']=28;
v['S']=29;
v['W']=30;
v['N']=31;
v['Z']=32;
v['B']=33;
v['F']=34;
T=read();
while(T--)
{
reset();
scanf("%s",st);
while(st[0]!='0')
{
if(st[0]>='1'&&st[0]<='9')
{
num[st[0]-'0'+v[st[1]]*9]--;
}
else num[v[st[0]]]--;
scanf("%s",st);
}
scanf("%s",st);
while(st[0]!='0')
{
if(st[0]>='1'&&st[0]<='9')
{
vab[st[0]-'0'+v[st[1]]*9]=1;
V[st[0]-'0'+v[st[1]]*9]=2;
}
else vab[v[st[0]]]=1,V[v[st[0]]]=2;
scanf("%s",st);
}
ans=0;
ans=max(ans,gsws());
ans=max(ans,get7());
memset(f,0,sizeof f);
f[1][0][0][0][0][0]=1;
for(int i=1;i<=34;i++)
for(int j=0;j<=4;j++)
for(int k=0;k<=1;k++)
for(int l=0;l<=4;l++)
for(int m=0;m<=4;m++)
for(int n=0;n<=4;n++)
{
if(!f[i][j][k][l][m][n]) continue;
tmp=f[i][j][k][l][m][n]; if(k==0&&n+2<=num[i])
f[i][j][1][l][m][n+2]=max(f[i][j][1][l][m][n+2],tmp);//雀头 if(j<4&&n+3<=num[i])
f[i][j+1][k][l][m][n+3]=max(f[i][j+1][k][l][m][n+3],tmp);//刻子 if(j<4&&i<28&&i%9!=1&&i%9!=2&&n+1<=num[i]&&m+1<=num[i-1]&&l+1<=num[i-2])//顺子
f[i][j+1][k][l+1][m+1][n+1]=max(f[i][j+1][k][l+1][m+1][n+1],tmp/c[num[i-2]][l]*c[num[i-2]][l+1]/c[num[i-1]][m]*c[num[i-1]][m+1]*V[i-2]*V[i-1]); if(i<34) f[i+1][j][k][m][n][0]=max(f[i+1][j][k][m][n][0],tmp*c[num[i]][n]*ksm(V[i],n));
if(k==1&&j==4) ans=max(ans,tmp*c[num[i]][n]*ksm(V[i],n));
}
printf("%lld\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}

 

 

 

 

T3 特技飞行

链接

 

 

问题很复杂,但好像有很多不相干的问题;比如专家加分是不会影响技术得分;

怎么求专家得分?

也就是说怎么求被观测到的交点数?

交点不可能\(n^2\)求,但交点个数范围是可行的;

两条线段相交地条件是在起点的顺序与在终点的不一样;也就是一对逆序对;

就可以在归并排序的同时求交点(找逆序对),这里的时间是\(O(nlogn+q)\)\(q\)是交点个数,因为求的每一个交点都是不同的;

 

 

求出所有交点之后,就要求在范围内的个数;但每个范围都是一个正方形旋转\(45\)度,不方便求解;

那就把所有点都旋转\(45\)度,再把每条边扩大根号\(2\)倍,当然每个点与原点形成的向量也要扩大,(应该也可以都不扩大),然后就是在一堆矩形求被包括的点,这里可以用扫描线;用差分树状数组维护;

 

 

第二个问题就是技术得分,首先发现一次擦身而过就是制造了一对逆序对,而对向交换没有改变顺序,最后要求顺序不变,所以完全可以全部进行对向交换,根据贪心,如果\(A>B\)就全部对向交换,否则尽量少对向交换,就是求最多可以擦身而过多少次;

 

 

我们将一个点与它的目的地点相连

比如\(1\)号飞机要去\(y\)最小的位置,而这个位置是\(2\)号飞机的目的地,就把\(1\)到\(2\)连起来;

这样就会得到很多环,而每一次对向交换可以将环上两个点缩成一个点,最后环都成了一个点就行了,这样每个点至少要对向交换\(len-1\)次,总共就需要交换\(n- 环数\)次,剩下的就是最多进行的擦身而过的次数;

 

 

\(Code\)

#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define Vector point
using namespace std;
inline int read()
{
int x=0,fl=1;char st=getchar();
while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
return x*fl;
}
struct point
{
double x,y;
void init(){ scanf("%lf%lf",&x,&y);}
point(double X=0,double Y=0):x(X),y(Y){}
bool operator <(point w) const
{
return x==w.x?y<w.y:x<w.x;
}
};
point operator +(point a,point b){return point(a.x+b.x,a.y+b.y);}
point operator -(point a,point b){return point(a.x-b.x,a.y-b.y);}
point operator *(point a,double b){return point(a.x*b,a.y*b);}
double operator *(point a,point b){return a.x*b.x+a.y*b.y;}
inline double cross(point a,point b){ return a.x*b.y-a.y*b.x;}
struct line
{
point s,t;int id;
};
inline point lipo(point a,point b,point c,point d)
{
Vector v1=b-a,v2=d-c,v3=c-a;
double t=cross(v3,v1)/cross(v1,v2);
return c+v2*t;
}
const int N=100005,M=500005;
int n,A,B,C,st,ed,jdnum,ans1,ans2;
line p[N],b[N];
point jd[M];
inline void qsort(int l,int r)
{
if(l==r) return ;
int mid=l+r>>1;
qsort(l,mid);qsort(mid+1,r);
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r)
{
if(p[i].t.y<p[j].t.y) b[k++]=p[i++];
else
{
b[k++]=p[j++];
for(int o=i;o<=mid;o++)
jd[++jdnum]=lipo(p[o].s,p[o].t,p[j-1].s,p[j-1].t);
}
}
while(i<=mid) b[k++]=p[i++];
while(j<=r) b[k++]=p[j++];
for(int o=l;o<=r;o++)
p[o]=b[o];
}
const double eps=1e-7;
int tot;
struct FT
{
int v[M+N+N];
void add(int x,int y)
{
for(;x<=tot;x+=x&-x) v[x]+=y;
}
int ask(int x)
{
int res=0;
for(;x>0;x-=x&-x) res+=v[x];
return res;
}
}ft;
struct scan
{
int m,numx;
double lsh[M+N+N];
struct L
{
double x,y1,y2;int w;
bool operator<(L B)const{return x<B.x;}
}c[M+N+N]; void pre()
{
for(int i=1;i<=jdnum;i++)
{
double x=jd[i].x,y=jd[i].y;
jd[i].x=x+y;jd[i].y=y-x;
lsh[++numx]=jd[i].y;
}
sort(jd+1,jd+1+jdnum);
m=read();
for(int i=1;i<=m;i++)
{
double x,y,r,X,Y;
scanf("%lf%lf%lf",&x,&y,&r);
X=x+y;Y=y-x;
point p1,p2,p3,p4;
// r=r*sqrt(2)/2*sqrt(2);
int i1=i+i-1,i2=i+i;
c[i1]=(L){X-r-eps,Y-r-eps,Y+r+eps,1};
c[i2]=(L){X+r+eps,Y-r-eps,Y+r+eps,-1};
lsh[++numx]=Y-r-eps;lsh[++numx]=Y+r+eps;
}
m<<=1;
sort(c+1,c+1+m);
sort(lsh+1,lsh+1+numx);
lsh[0]=-1e10;
for(int i=1;i<=numx;i++)
if(fabs(lsh[i]-lsh[i-1])>eps)
lsh[++tot]=lsh[i];
for(int i=1;i<=jdnum;i++)
jd[i].y=lower_bound(lsh+1,lsh+tot+1,jd[i].y)-lsh;
for(int i=1;i<=m;++i)
{
c[i].y1=lower_bound(lsh+1,lsh+tot+1,c[i].y1)-lsh;
c[i].y2=lower_bound(lsh+1,lsh+tot+1,c[i].y2)-lsh;
}
}
int work()
{
pre();
int res=0;
for(int i=1,j=1;i<=jdnum;i++)
{
while(j<=m&&c[j].x<=jd[i].x)
{
ft.add(c[j].y1,c[j].w);
ft.add(c[j].y2+1,-c[j].w);
j++;
}
if(ft.ask(jd[i].y)) ++res;
}
return res;
}
}SC;
int fa[N];
inline int get(int x)
{
return x==fa[x]?x:fa[x]=get(fa[x]);
}
inline void merge(int x,int y)
{
int f1=get(x),f2=get(y);
if(f1!=f2) fa[f1]=f2;
}
inline int getcir()
{
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=n;i++) merge(i,p[i].id);
int res=0;
for(int i=1;i<=n;i++) if(get(i)==i) res++;
return jdnum-n+res;
}
int main()
{
freopen("aerobatics.in","r",stdin);
freopen("aerobatics.out","w",stdout);
n=read();A=read();B=read();C=read();st=read();ed=read();
for(int i=1;i<=n;i++) scanf("%lf",&p[i].s.y),p[i].s.x=st;
for(int i=1;i<=n;i++) scanf("%lf",&p[i].t.y),p[i].t.x=ed,p[i].id=i;
qsort(1,n);
int numc=SC.work();
ans1=numc*C+jdnum*A;ans2=ans1;
ans2+=(B-A)*getcir();
if(ans1>ans2) swap(ans1,ans2);
printf("%d %d",ans1,ans2);
fclose(stdin);
fclose(stdout);
return 0;
}

 

 

 

T4 逼死强迫症

链接

 

 

先考虑全是\(1*2\)的砖,\(g[i]\)代表长度为\(i\)的路的铺砖方法;经过画图发现\(g[i]=g[i-2]+g[i-1]\),就是在\(i-2\)基础上加两块横着的,或在\(i-1\)的基础上加一块竖着的;

\(g\)就是一个斐波那契数列;

 

再考虑加上\(1*1\)的;

如果两块砖都没在边上的话,一定是中间一个子问题,两边用横砖或竖砖补齐,为了不重复,用上一段的办法应该产生贡献\(f[i-1]+f[i-2]\);

另外如果有一块砖在最边上(比如在最左边),那么两块砖之间只有一种排列方式,而在右边的那块砖的右边可以随意摆放,这里就可以利用\(g\)了;

 

枚举另一块砖所在的地方,转化一下就可以计算\(g\)的前缀和记为\(h\);

\[f[i]=f[i-1]+f[i-2]+2*h[i-3]
\]

二倍是由于左右都可;

 

又有斐波那契数列的性质\(h[i]=g[i+2]-1\)

所以

\[f[i]=f[i-1]+f[i-2]+2*g[i-1]-2
\]

 

然后就是矩阵快速幂;

 

\(Code\)

#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define Vector point
using namespace std;
inline int read()
{
int x=0,fl=1;char st=getchar();
while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
return x*fl;
}
const int mod=1e9+7;
int T,n;
int ans;
struct Matrix
{
int v[5][5];
Matrix(){ memset(v,0,sizeof v);}
Matrix operator *(Matrix b)
{
Matrix c;
for(int i=0;i<5;i++)
for(int j=0;j<5;j++)
for(int k=0;k<5;k++)
c.v[i][j]=(c.v[i][j]+(ll)v[i][k]*b.v[k][j]%mod)%mod;
return c;
}
}a,b,c,d;
inline Matrix ksm(Matrix x,int y)
{
Matrix res=x;y--;
while(y)
{
if(y&1) res=res*x;
x=x*x;
y>>=1;
}
return res;
}
int main()
{
freopen("obsession.in","r",stdin);
freopen("obsession.out","w",stdout); T=read();
a.v[0][0]=a.v[0][1]=a.v[1][0]=a.v[2][2]=a.v[2][3]=a.v[3][2]=a.v[4][4]=1;a.v[2][0]=2;a.v[4][0]=mod-1;
b.v[0][0]=2;b.v[0][2]=3;b.v[0][3]=2;b.v[0][4]=2;
while(T--)
{
n=read();
if(n<3) printf("0\n");
else
{
if(n==3) printf("2\n");
else
{
n-=3;
c=a;d=b;
printf("%d\n",(d*ksm(c,n)).v[0][0]);
}
}
}
fclose(stdin);
fclose(stdout);
}

T5 旅行者

链接

 

题解的做法:将所有关键点分成两部分,一部分与一个源点连边权为\(0\)的边,另一部分与汇点连边权为\(0\)的边,跑一次源点到汇点的最短路,得到的就是两个部分的最短路,但同一部分内的最短路也可能是答案,我们考虑一个划分方案:按二进制下的每一位分,相同的在同一部分,因为答案点对一定是不一样的,就一定有一个二进制位不一样,这样不失一般性;时间是\(O(Tnlog^2n)\);

 

好像会\(T\)

求关键点多源最短路有一个方法,跑一次最短路算法计算出每个点,离它最近的关键点(\(col\))和距离(\(d\));

再枚举每一条边\((u,v)\),如果\(col[u]!=col[v]\)就用\(d[u]+d[v]+V(u,v)\)更新答案;

 

这也不失一般性;因为最短的那条路径一定存在上述的一条边;

 

但这道题是有向图;我们必须分别求出去和会的最近距离,才是一条满足的路径;所以必须建反图再跑一次;时间是\(O(Tnlogn)\)

\(Code\)

#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define Vector point
using namespace std;
inline int read()
{
int x=0,fl=1;char st=getchar();
while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
return x*fl;
}
const int N=1e6+20;
int T,n,m,k;
int ci[N],isc[N];
int v[N];
ll ans;
struct graph
{
int head[N],cnt;
ll d[N];
int v[N],col[N];
struct skr
{
int to,nxt,v;
}a[N];
inline void add(int x,int y,int z)
{
a[++cnt].to=y;a[cnt].nxt=head[x];a[cnt].v=z;head[x]=cnt;
}
void clear(){cnt=0; memset(head,0,sizeof head);}
priority_queue<pair<ll,int> >q;
inline void dij()
{
for(int i=1;i<=n;i++) d[i]=1e18;
for(int i=1;i<=n;i++) v[i]=0;
while(q.size()) q.pop();
for(int i=1;i<=k;i++)
d[ci[i]]=0,col[ci[i]]=ci[i],q.push(mp(0,ci[i]));
while(q.size())
{
int x=q.top().second;q.pop();
if(v[x]) continue;
v[x]=1;
for(int i=head[x];i;i=a[i].nxt)
{
int to=a[i].to;
if(d[to]>d[x]+a[i].v)
{
d[to]=d[x]+a[i].v;
col[to]=col[x];
q.push(mp(-d[to],to));
}
}
}
}
}A,B; int main()
{
freopen("tourist.in","r",stdin);
freopen("tourist.out","w",stdout);
T=read();
while(T--)
{
n=read();m=read();k=read();
A.clear();B.clear();
for(int i=1;i<=m;i++)
{
int x=read(),y=read(),z=read();
if(x==y) continue;
A.add(x,y,z);
B.add(y,x,z);
}
for(int i=1;i<=k;i++)
ci[i]=read();
A.dij();B.dij();
ans=1e18;
for(int x=1;x<=n;x++)
for(int i=A.head[x];i;i=A.a[i].nxt)
{
int y=A.a[i].to;
if(A.col[x]!=B.col[y]) ans=min(ans,A.d[x]+B.d[y]+A.a[i].v);
}
printf("%lld\n",ans);
}
fclose(stdin);
fclose(stdout);
}

 

 

 

T6 旧词

链接

 

先考虑\(k=1\)的情况;\(lca\)的深度就是根节点到它的距离,我们把贡献分摊到每个节点上;

先把询问按\(x\)排序,考虑一个一个地加,加入一个点就把这个点到根节点的路径的点权都加1;

查询,就询问\(y\)到根节点的权值和;可以用\(LCT\)维护;

 

\(k!=1\) 我们还想让一个点到根节点的路径和等于贡献(深度的\(k\)次方),那就差分一下,每次加值就加上\((d)^k-(d-1)k\)这也可以用\(LCT\)维护;

 

\(Code\)

 

 

#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define Vector point
using namespace std;
inline ll read()
{
ll x=0,fl=1;char st=getchar();
while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
return x*fl;
}
const ll N=50005,mod=998244353;
ll n,m,k;
ll d[N],b[N];
struct LCT
{
#define ls ch[x][0]
#define rs ch[x][1]
ll fa[N],ch[N][2];
ll tag[N],v[N],s[N],sz[N];ll sta[N];
bool nort(ll x){ return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
void upd(ll x){ s[x]=(s[ls]+s[rs]+(ll)v[x]*b[x])%mod;sz[x]=sz[ls]+sz[rs]+b[x];}
void rev(ll x,ll y){ s[x]=(s[x]+sz[x]*y%mod)%mod;tag[x]=(tag[x]+y)%mod;v[x]=(v[x]+y)%mod;}
void pd(ll x)
{
if(tag[x])
{
if(ls) rev(ls,tag[x]);
if(rs) rev(rs,tag[x]);
tag[x]=0;
}
}
void rotate(ll x)
{
ll y=fa[x],ys=(ch[y][1]==x);
ll R=fa[y];
ll B=ch[x][ys^1];
if(nort(y)) ch[R][ch[R][1]==y]=x; ch[x][ys^1]=y; ch[y][ys]=B;
if(B) fa[B]=y; fa[x]=R; fa[y]=x;
upd(y);upd(x);
}
void splay(ll x)
{
ll y=x,z,top=0;sta[++top]=y;
while(nort(y)) y=fa[y],sta[++top]=y;
while(top) pd(sta[top--]);
while(nort(x))
{
y=fa[x];z=fa[y];
if(nort(y))
rotate((ch[z][1]==y)==(ch[y][1]==x)?y:x);
rotate(x);
}
}
void access(ll x)
{
for(ll y=0;x;y=x,x=fa[x])
splay(x),rs=y,upd(x);
}
// void makert(ll x){ access(x);splay(x);rev(x);}
void split(ll y){ access(y);splay(y);}
void add(ll x)
{
split(x);rev(x,1);
}
ll ask(ll x)
{
split(x);return s[x];
}
}lct;
struct Qry
{
ll r,z,id;
bool operator<(Qry w)const
{
return r<w.r;
}
}q[N];
ll ans[N],pos;
inline ll ksm(ll x,ll y)
{
ll res=1;
while(y){ if(y&1) res=(ll)res*x%mod;x=(ll)x*x%mod;y>>=1;}
return res;
}
struct skr
{
ll to,nxt;
}a[N<<1];
ll head[N],cnt;
inline void add(ll x,ll y)
{
a[++cnt].to=y;a[cnt].nxt=head[x];head[x]=cnt;
}
inline void dfs(ll x)
{
for(ll i=head[x];i;i=a[i].nxt)
{
ll y=a[i].to;
if(d[y]) continue;
d[y]=d[x]+1;
dfs(y);
}
}
int main()
{
freopen("poetry.in","r",stdin);
freopen("poetry.out","w",stdout);
n=read();m=read();k=read();
for(ll i=2;i<=n;i++)
{
ll x=read();
add(x,i);
add(i,x);
lct.fa[i]=x;
}
d[1]=1;
dfs(1);
for(ll i=1;i<=n;i++) d[i]=ksm(d[i],k);
for(ll i=1;i<=n;i++)
b[i]=(d[i]-d[lct.fa[i]]+mod)%mod;
for(ll i=1;i<=m;i++)
{
ll x=read(),y=read();
q[i]=(Qry){x,y,i};
}
sort(q+1,q+1+m);
for(ll i=1;i<=m;i++)
{
while(pos+1<=q[i].r) pos++,lct.add(pos);
ans[q[i].id]=lct.ask(q[i].z)%mod;
}
for(ll i=1;i<=m;i++)
printf("%lld\n",ans[i]);
fclose(stdin);
fclose(stdout);
}

GXOI&GZOI的更多相关文章

  1. Solution Set - 《赏竹而格之》

    1.「GXOI / GZOI 2019」「洛谷 P5304」旅行者   Link & Submission.   经典二进制分组,没啥好说的. 2. 「SDOI 2019」「洛谷 P5361」 ...

  2. 【BZOJ5505】[GXOI/GZOI2019]逼死强迫症(矩阵快速幂)

    [BZOJ5505][GXOI/GZOI2019]逼死强迫症(矩阵快速幂) 题面 BZOJ 洛谷 题解 如果没有那两个\(1*1\)的东西,答案就是斐波那契数,可以简单的用\(dp\)得到. 大概是设 ...

  3. [LOJ3087][GXOI/GZOI2019]旅行者——堆优化dijkstra

    题目链接: [GXOI/GZOI2019]旅行者 我们考虑每条边的贡献,对每个点求出能到达它的最近的感兴趣的城市(设为$f[i]$,最短距离设为$a[i]$)和它能到达的离它最近的感兴趣的城市(设为$ ...

  4. [LOJ3088][GXOI/GZOI2019]旧词——树链剖分+线段树

    题目链接: [GXOI/GZOI2019]旧词 对于$k=1$的情况,可以参见[LNOI2014]LCA,将询问离线然后从$1$号点开始对这个点到根的路径链修改,每次询问就是对询问点到根路径链查询即可 ...

  5. [LOJ3086][GXOI/GZOI2019]逼死强迫症——递推+矩阵乘法

    题目链接: [GXOI/GZOI2019]逼死强迫症 设$f[i][j]$表示前$i$列有$j$个$1*1$的格子的方案数,那么可以列出递推式子: $f[i][0]=f[i-1][0]+f[i-2][ ...

  6. [LOJ3084][GXOI/GZOI2019]宝牌一大堆——DP

    题目链接: [GXOI/GZOI2019]宝牌一大堆 求最大值容易想到$DP$,但如果将$7$种和牌都考虑进来的话,$DP$状态不好设,我们将比较特殊的七小对和国士无双单独求,其他的进行$DP$. 观 ...

  7. P5305 [GXOI/GZOI2019]旧词

    题目地址:P5305 [GXOI/GZOI2019]旧词 这里是官方题解 \[\sum_{i \leq x}^{}\ depth(lca(i,y))^k\] \(k = 1\) 求的是 \(\sum_ ...

  8. P5304 [GXOI/GZOI2019]旅行者

    题目地址:P5304 [GXOI/GZOI2019]旅行者 这里是官方题解 一个图 \(n\) 点 \(m\) 条边,里面有 \(k\) 个特殊点,问这 \(k\) 个点之间两两最短路的最小值是多少? ...

  9. P5303 [GXOI/GZOI2019]逼死强迫症

    题目地址:P5303 [GXOI/GZOI2019]逼死强迫症 这里是官方题解 初步分析 从题目和数据范围很容易看出来这是一个递推 + 矩阵快速幂,那么主要问题在于递推的过程. 满足条件的答案一定是以 ...

随机推荐

  1. Python--day62--ORM的使用

    4.Django里ORM的使用 1,手动创建数据库 2,在settings.py里面,配置数据库的连接信息 3,在项目/__init__.py告诉Django用pymysql模块代替MySQLdb(不 ...

  2. Python--day28--摘要算法

    摘要算法:

  3. HDU 2717 宽搜第一题、

    题意:求n到k的最小路径,  n有三种变法 n+1,n-1或者2*n: 贴个广搜的模版在这里把.... 总结一下:一般涉及到求最短路的话用宽搜 #include<iostream> #in ...

  4. Native memory allocation (mmap) failed to map xxx bytes for committing reserved memory

    遇到问题 在服务器上运行 nexus 出现Native memory allocation (mmap) failed to map 838860800 bytes for committing re ...

  5. 高可用之nginx配置文件详解

    #user nobody; worker_processes 1;##工作线程数,一般和cpu的核数相同:可通过ps -ef | nginx查看线程数 #配置错误日志位置 #error_log log ...

  6. js基础——基本包装类型

    1.基本包装类型String   var bz = new String("Li.Linda"); //引用类型(object)         bz.name= bz.subst ...

  7. C# 程序集数量对软件启动性能的影响

    本文通过很多的数据测试分析在一个项目引用很多个外部项目和将外部项目的类合并到一个项目之间的启动性能的不同. 通过分析知道了如果一个项目引用了很多项目,而且在启动过程会全部调用这些项目,这时的软件性能会 ...

  8. C# 字典 Dictionary 的 TryGetValue 与先判断 ContainsKey 然后 Get 的性能对比

    本文使用 benchmarkdotnet 测试字典的性能,在使用字典获取一个可能存在的值的时候可以使用两个不同的写法,于是本文分析两个写法的性能. 判断值存在,如果值存在就获取值,可以使用下面两个不同 ...

  9. 关于electron中入口文件main.js一些重要参数(持续更新maybe)

    const {app, BrowserWindow} = require('electron') const path = require('path') let mainWindow functio ...

  10. 编写jQuery插件的方法和注意点

    编写jQuery插件的方法和注意点 插件的种类 jQuery的插件主要分为3种类型. 1. 封装对象方法的插件 这种插件是将对象方法封装起来,用于对通过选择器获取的jQuery对象进行操作,是最常见的 ...