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. get_magic_quotes_gpc() PHP转义的真正含义

    如何正确的理解PHP转 义是一个初学者比较困扰的问题.我们今天为大家简要的讲述了PHP转义的具体含义,希望有所帮助.PHP转义一直困扰着我, 今天认真的看了一下PHP手册, 终于解决了. 在PHP中默 ...

  2. ios html5头部无法固定的问题(安卓正常)

    需求:头部菜单导航固定,中间正文可以拉动,在安卓手机正常,在ios上下拉的时候头部被带下来,有卡顿用户体验也不会,解决方法如下: 有问题的布局代码 <div class="page&q ...

  3. Spring Boot 集成日志logback + 控制台打印SQL

    一: 控制台打印SQL application.properties中添加如下即可在控制台打印sql logging.level.com.fx.fxxt.mapper=debug 二:日志 因为Spr ...

  4. 严重: Servlet.service() for servlet [jsp] threw exception java.lang.NullPointerException

    五月 04, 2018 11:53:24 上午 org.apache.catalina.core.ApplicationDispatcher invoke 严重: Servlet.service() ...

  5. 高级教程: 作出动态决策和 Bi-LSTM CRF 重点

    动态 VS 静态深度学习工具集 Pytorch 是一个 动态 神经网络工具包. 另一个动态工具包的例子是 Dynet (我之所以提这个是因为使用 Pytorch 和 Dynet 是十分类似的. 如果你 ...

  6. [转]vue原理简介

    写vue也有一段时间了,对vue的底层原理虽然有一些了解,这里总结一下. vue.js中有两个核心功能:响应式数据绑定,组件系统.主流的mvc框架都实现了单向数据绑定,而双向绑定无非是在单向绑定基础上 ...

  7. H3C 显示OSPF邻居信息

  8. linux inode 结构

    inode 结构由内核在内部用来表示文件. 因此, 它和代表打开文件描述符的文件结构是不 同的. 可能有代表单个文件的多个打开描述符的许多文件结构, 但是它们都指向一个单个 inode 结构. ino ...

  9. dotnet core 使用 CoreRT 将程序编译为 Native 程序

    现在微软有一个开源项目 CoreRT 能通过将托管的 .NET Core 编译为单个无依赖的 Native 程序 这个项目现在还没发布,但是能尝试使用,可以带来很多的性能提升 使用 CoreRT 发布 ...

  10. Linux 内核kobject 缺省属性

    当被创建时, 每个 kobject 被给定一套缺省属性. 这些属性通过 kobj_type 结构来指定. 这个结构, 记住, 看来如此: struct kobj_type { void (*relea ...