【口胡】
快速幂一位一位算就行了
#include<bits/stdc++.h>
using namespace std;
char a[100010];
int rest,now,K,t;
int quick_pow(int a,int b,int mod){
int ret=1;
while(b){
if(b&1) ret=ret*a%mod;
a=a*a%mod;
b>>=1;
}
return ret;
}
int main(){
/* freopen("kbased.in","r",stdin);
freopen("kbased.out","w",stdout);*/
scanf("%d",&t);
while(t--){
rest=0;
scanf("%d",&K);
scanf("%s",a);
int len=strlen(a);
for(int i=len-1;i>=0;--i){
if(a[i]>='0'&&a[i]<='9') now=a[i]-'0';
if(a[i]>='A'&&a[i]<='F') now=a[i]-'A'+10;
rest=(rest+quick_pow(K,len-i-1,K-1)*now)%(K-1);
}
if(rest==0) printf("yes\n");
else printf("no\n");
}
return 0;
}
————————————————————————————————————————————————————————
【口胡】
SCC+DFS
具体做法:先缩点,整个图变成一颗树,然后两次DFS求出直径AB,
到x的最远距离是MAX(dis(x,A),dis(x,B))。
小证一下正确性:
假设AB是这颗树的直径,并且存在一个结点C,使得 x到C的距离 大于 max(x到A的距离,x到B的距离)。
不妨设x到A的距离大于x到B的距离。那么直径就会变成AC(红色部分),矛盾。
所以到x的最远距离是max(dis(x,A),dis(x,B))。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=20010;
const int maxm=400040;//双向边,开两倍!!
int root,N,M,a,b,c,cnt=0,ent=0,top=0,sz=0,ddd=0,mx=0;
int st[maxn],ins[maxn],low[maxn],dfsn[maxn],belong[maxn];//缩点用
int Head[maxn],Next[maxm],V[maxm],W[maxm];//原图信息
int head[maxn],next[maxm],v[maxm],w[maxm];//缩完后,每个集合是一个点,存集合信息
ll disa[maxn],dis[maxn];
void Add(int u,int v,int w){
++ent;
Next[ent]=Head[u];
V[ent]=v;
W[ent]=w;
Head[u]=ent;
}
void addedge(int u,int v,int w){Add(u,v,w),Add(v,u,w);}
void addd(int U,int V,int W){
++ddd;
next[ddd]=head[U];
v[ddd]=V;
w[ddd]=W;
head[U]=ddd;
}
void adddedge(int u,int v,int w){addd(u,v,w),addd(v,u,w);}
//tarjan模板,belong[i]表示i所在集合
void tarjan(int u,int fa){
st[++top]=u,ins[u]=1;
dfsn[u]=low[u]=++sz;
for(int i=Head[u];i!=-1;i=Next[i]){
int v=V[i];
if(v==fa) continue;
if(!dfsn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
}
else if(ins[v]) low[u]=min(low[u],dfsn[v]);
}
if(dfsn[u]==low[u]){
int j;cnt+=1;
while(j!=u){
j=st[top--];
ins[j]=0;
belong[j]=cnt;
}
}
}
//两次dfs,求直径。利用了刚刚证的性质
//第一次dfs随便找一个点为根,搜到直径的一个端点A。 搜完后把mx值和dis值要初始化为0,供下次搜索使用 。
//第二次dfs以A为根,搜索过程中求距离:dis[i]表示i到A的距离。搜完后得到另一个端点B。
void dfs(int u,int fa){
for(int i=head[u];i!=-1;i=next[i]){
int V=v[i];
if(V==fa) continue;
dis[V]=dis[u]+w[i];
if(dis[V]>mx) mx=dis[V],root=V;
dfs(V,u);
}
}
//求每个点到B的距离:disa[i]表示i到B的距离。
void dfss(int u,int fa){
for(int i=head[u];i!=-1;i=next[i]){
int V=v[i];
if(V==fa) continue;
disa[V]=disa[u]+w[i];
dfss(V,u);
}
}
void read(int &x){
x=0;char ch=getchar();
while(ch>'9'||ch<'0') ch=getchar();
while(ch<='9'&&ch>='0') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
}
int main(){
memset(head,-1,sizeof(head));
memset(next,-1,sizeof(next));
memset(Head,-1,sizeof(Head));
memset(Next,-1,sizeof(Next));
read(N),read(M);
for(int i=1;i<=M;++i){
read(a),read(b),read(c);
addedge(a,b,c);
}
for(int i=1;i<=N;++i)
if(!dfsn[i]) tarjan(i,0);
/*
对于每个点,如果与它有连线的点和它
不在同一个集合,就把这两个集合连起来。
*/
for(int i=1;i<=N;++i)
for(int j=Head[i];j!=-1;j=Next[j])
if(belong[i]!=belong[V[j]])
addd(belong[i],belong[V[j]],W[j]);
dfs(1,-1);
mx=0,memset(dis,0,sizeof(dis));
dfs(root,-1);
mx=0;
dfss(root,-1);
for(int i=1;i<=N;++i) printf("%lld\n",max(dis[belong[i]],disa[belong[i]]));
}
【口胡】
树形DP做法:
int s[maxn],S[maxn],cd[maxn],g[maxn],ans[maxn];
/*
s[i]表示从儿子转移过来
S[i]表示从哪个儿子转移过来
cd[i]表示儿子中的次大值
g[i]表示从父亲转移过来
ans[i]存答案
*/
void dfs(int u,int fa){
for(int i=head[u];i!=-1;i=next[i]) if(v[i]!=fa)
{
dfs(v[i],u);
if(s[v[i]]+w[i]>s[u]){
cd[u]=s[u];
s[u]=s[v[i]]+w[i];
S[u]=v[i];
}
else cd[u]=max(cd[u],s[v[i]]+w[i]);
}
}
void DFS(int u,int fa){
for(int i=head[u];i!=-1;i=next[i]) if(v[i]!=fa)
{
if(v[i]==S[u])
g[v[i]]=max(g[u],cd[u])+w[i];
else
g[v[i]]=max(g[u],s[u])+w[i];
DFS(v[i],u);
}
}
先从儿子转移,s[i]表示i到叶结点的最大值。
再从父亲转移,g[i]表示从i的父结点到i的最大值。
记录一下s[i]的次大,防止g[i]转移的时候出问题,如下:
若s[u]从v转移过来,那么g[v]就不能用max(g[u],s[u])+w[i]转移,这样会把w[i]算两次。所以在s转移的时候记录一个次大值。当出现这种情况的时候用次大值转移就行了--g[v]=max(g[u],cd[u])+w[i]。
——————————————————————————————————————————————————————