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. Oracle的dual是什么东西啊

    原文:https://zhidao.baidu.com/question/170487574.html?fr=iks&word=dual&ie=gbk Oracle的dual是什么东西 ...

  2. 【原生JS】写最简单的图片轮播

    非常简单的一个大图轮播,通过将控制显示位置来进行轮播效果,写来给正在学习的新手朋友们参考交流. 先看效果:(实际效果没有这么快) 先看布局: <div id="display" ...

  3. H3C PPP显示与调试

  4. iptables端口映射

    见上节透明代理设置 #iptables -t nat -A PREROUTING -i eth0 -p tcp -s 192.168.62.0/24 --dport 80 -j REDIRECT -- ...

  5. jQuery中动态创建、添加元素的方法总结

    <input type="button" value="创建元素" id="btn"> <div id="box ...

  6. HDU 2601

    题意:给出一个n求出n=i*j+i+j共有几种组合,i,j>0. 开始挺傻的.没想到化成因式的乘积.- - . 思路:i*j+i+j=(i+1)*(j+1)=n+1 #include<io ...

  7. 递归求gcd(a,b)

    int gcd(int a,int b) { ) return a; else return gcd(b,a%b); }

  8. 一次操作系统报错OutOfMemory Error的处理记录

    在启动公司内嵌的tomcat容器时出现报错, 如下: # There is insufficient memory for the Java Runtime Environment to contin ...

  9. 2019-9-2-Visual-Studio-自定义项目模板

    title author date CreateTime categories Visual Studio 自定义项目模板 lindexi 2019-09-02 12:57:38 +0800 2018 ...

  10. Lavarel之环境配置 .env

    .env 文件位于项目根目录下,作为全局环境配置文件. 1. 配置参数 // 运行环境名称 APP_ENV=local // 调试模式,开发阶段启用,上线状态禁用. APP_DEBUG=true // ...