T1:


很随便的就能想出来,设 f(i) 为各个位数和为i的数的个数,g(i) 为 。。。。和为i的数的和,然后可以发现递推的系数都是常数,矩阵转移即可。
但是 m^n 可能要高精度表示?? 别忘了可以在算出 d(m^i) 的答案矩阵的基础上 直接m次幂直接算出 d(m^(i+1)) 的答案矩阵。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
#define M(x) memset(x,0,sizeof(x))
const int ha=1e9,maxn=23;
inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
void W(int x,int L){ if(L) W(x/10,L-1); putchar(x%10+'0');}
int n,m,ans;
struct node{
int a[maxn][maxn];
inline void clear(){ M(a);}
inline void Base(){ clear(); for(int i=1;i<=18;i++) a[i][i]=1;}
node operator *(const node &u)const{
node r; r.clear();
for(int k=1;k<=18;k++)
for(int i=1;i<=18;i++)
for(int j=1;j<=18;j++) ADD(r.a[i][j],a[i][k]*(ll)u.a[k][j]%ha);
return r;
}
}b,ANS;
inline void build(){
b.clear();
for(int i=1;i<9;i++) ADD(b.a[i][i+1],1);
for(int i=10;i<18;i++) ADD(b.a[i][i+1],1);
for(int i=1;i<=9;i++) ADD(b.a[i][1],1),ADD(b.a[i][10],i);
for(int i=10;i<=18;i++) ADD(b.a[i][10],10);
}
inline void calc(){
ANS.Base();
node x=b; int y=m;
for(;y;y>>=1,x=x*x) if(y&1) ANS=ANS*x;
ADD(ans,ANS.a[1][10]);
b=ANS;
}
inline void solve(){
build();
for(int i=1;i<=n;i++) calc();
}
int main(){
// freopen("num.in","r",stdin);
// freopen("num.out","w",stdout);
scanf("%d%d",&n,&m);
solve();
if(n<=3&&m<=2) printf("%d
",ans);
else W(ans,8),puts("");
return 0;
}
T2:


真是深黑数论题啊23333
可以发现底数是 n%p ,指数是 2n % (p-1)。
所以我们可以先设 now = n % p,然后求出满足 now^y + now^m = x (mod p) 的y,并且要保证y是偶数(因为p-1肯定是偶数,2在这个同余系下是没有逆元的) (一个离散对数就行了)
所以我们现在就得到了 : n % p = now ; n % (p-1) = y/2。
这个玩意手玩中国剩余定理就行了,都不用上模板23333(但注意模数是long long的时候要用快速乘)
好像忘了说now咋找了。。。怕出题人卡我我是随机的now,效果还不错2333
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<ctime>
#include<map>
#define ll long long
using namespace std;
map<int,int> mmp;
int b,m,p,now,sz;
ll ans;
inline int add(int x,int y,const int ha){ x+=y; return x>=ha?x-ha:x;}
inline int ksm(int x,int y,const int ha){
int an=1;
for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha;
return an;
}
inline ll ADD(ll x,ll y,const ll ha){ x+=y; return x>=ha?x-ha:x;}
inline ll ksc(ll x,ll y,const ll ha){
ll an=0;
for(;y;y>>=1,x=ADD(x,x,ha)) if(y&1) an=ADD(an,x,ha);
return an;
}
inline ll fpow(ll x,ll y,const ll ha){
ll an=1;
for(;y;y>>=1,x=ksc(x,x,ha)) if(y&1) an=ksc(an,x,ha);
return an;
}
inline bool can(){
mmp.clear();
bool flag=0;
int u=add(b,p-ksm(now,m,p),p),v=1,inv=ksm(ksm(now,sz,p),p-2,p);
for(int i=0;i<sz;i++,v=v*(ll)now%p) if(!mmp.count(v)) mmp[v]=i;
v=u; int z;
for(int i=0;i<sz;i++,v=v*(ll)inv%p) if(mmp.count(v)&&!((i*sz+mmp[v])&1)){
z=i*sz+mmp[v],flag=1;
break;
}
if(!flag) return 0;
z>>=1;
ll to=p*(ll)(p-1);
ans=ADD(ksc(z,ksc(p,p,to),to),ksc(now,ksc(p-1,p-1,to),to),to);
return 1;
}
inline void solve(){
while(1){
now=rand()%p;
if(now) if(can()) break;
}
}
int main(){
// freopen("theory.in","r",stdin);
// freopen("check.out","w",stdout);
srand(time(0));
scanf("%d%d%d",&b,&m,&p),sz=sqrt(p)+3;
solve();
printf("%lld
",ans);
// if(add(ksm(ans%p,ans%(p-1)+ans%(p-1),p),ksm(ans%p,m,p),p)==b%p) puts("yes");
// else puts("no");
return 0;
}
T3:
题目已经传到我校OJ上了: http://lifecraft-mc.com:81/problem/1232
显然这是一个套路题,我们可以很轻松的通过dfs序把子树询问放到线段树的一个区间里,那么现在的问题是access的时候该怎么维护呢?
当我们要把一个点和它爸爸之间的边从轻边变成重边的时候,显然这个点的子树到根的轻边数都会-1,线段树区间加法就行了(可能标记永久化会快一点,反正我写的这个)。
但如果父亲原来就有重儿子的话,那条边是会变成轻边的,所以那颗子树对应的区间会区间+1。
当然不能直接修改splay的根对应的节点,因为重链的顶端对应的节点是splay中序遍历最小的节点,所以还需要写一个找重链顶端的函数。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<ctime>
#define ll long long
using namespace std;
#define D double
#define mid (l+r>>1)
#define lc (o<<1)
#define rc ((o<<1)|1)
const int maxn=150005;
int hd[maxn],ne[maxn*2],to[maxn*2],num;
int f[maxn],ch[maxn][2],n,m,dc,dfn[maxn];
int le,ri,tag[maxn*4],siz[maxn],w,Q,pos;
ll sum[maxn*4],ans;
char C;
inline void add(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num;}
inline int read(){
int x=0; char ch=getchar();
for(;!isdigit(ch);ch=getchar());
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return x+1;
}
void dfs(int x,int fa){
f[x]=fa,dfn[x]=++dc,siz[x]=1;
for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa){
dfs(to[i],x);
siz[x]+=siz[to[i]];
}
}
inline bool isroot(int x){ return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
inline int Get(int x){ return ch[f[x]][1]==x;}
inline void maintain(int x){ }
inline void rotate(int x){
int fa=f[x],ffa=f[fa],tp=Get(x);
ch[fa][tp]=ch[x][tp^1],f[ch[fa][tp]]=fa;
ch[x][tp^1]=fa,f[fa]=x;
f[x]=ffa;
if(ch[ffa][0]==fa||ch[ffa][1]==fa) ch[ffa][ch[ffa][1]==fa]=x;
maintain(fa),maintain(x);
}
inline void splay(int x){
for(;!isroot(x);rotate(x))
if(!isroot(f[x])) rotate(Get(f[x])==Get(x)?f[x]:x);
}
inline int getroot(int x){
while(ch[x][0]) x=ch[x][0];
return x;
}
void update(int o,int l,int r){
if(l>=le&&r<=ri){ sum[o]+=w*(ll)(r-l+1),tag[o]+=w; return;}
if(le<=mid) update(lc,l,mid);
if(ri>mid) update(rc,mid+1,r);
sum[o]=sum[lc]+sum[rc]+tag[o]*(ll)(r-l+1);
}
void query(int o,int l,int r,int Tag){
if(l>=le&&r<=ri){ ans+=sum[o]+Tag*(ll)(r-l+1); return;}
if(le<=mid) query(lc,l,mid,Tag+tag[o]);
if(ri>mid) query(rc,mid+1,r,Tag+tag[o]);
}
inline void access(int x){
for(int pre=0,now;x;pre=x,x=f[x]){
splay(x);
now=ch[x][1],ch[x][1]=0;
if(now){
splay(now),now=getroot(now);
le=dfn[now],ri=le+siz[now]-1;
w=1,update(1,1,n);
}
if(pre){
now=getroot(pre);
le=dfn[now],ri=le+siz[now]-1;
w=-1,update(1,1,n);
}
ch[x][1]=pre;
// maintain(x);
}
}
inline void solve(){
for(int i=2;i<=n;i++) le=dfn[i],ri=le+siz[i]-1,w=1,update(1,1,n);
scanf("%d",&Q);
while(Q--){
C=getchar();
while(C!='O'&&C!='q') C=getchar();
pos=read();
if(C=='q'){
ans=0,le=dfn[pos],ri=le+siz[pos]-1;
query(1,1,n,0),printf("%.0lf
",ans/(D)siz[pos]+1e-9);
}
else access(pos);
}
}
int main(){
// freopen("tong.in","r",stdin);
// freopen("ans.out","w",stdout);
scanf("%d",&n);
int uu,vv;
for(int i=1;i<n;i++) uu=read(),vv=read(),add(uu,vv),add(vv,uu);
dfs(1,0);
solve();
return 0;
}