namespace T{
vector <int> e[maxn * 2];
int tag[maxn * 2];
void adde(int x,int y){ //cout<
e[x].PB(y) , e[y].PB(x); }
}
namespace G{
node e[maxn * 2];
int head[maxn],cnt,dfn[maxn],fa[maxn],dfstime,used[maxn];
void adde(int x,int y){
// if ( x < y ) cout<
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
void dfs(int x){
dfn[x] = ++dfstime;
for(int i = head[x] ; i ; i = e[i].next){
if ( e[i].to == fa[x] ) continue;
if ( dfn[e[i].to] ){
if ( dfn[e[i].to] > dfn[x] ) continue;
//find_circle
++tot , T::tag[tot] = 1;
int z = x;
while ( z != e[i].to ){
T::adde(tot,z);
used[z] = 1;
z = fa[z];
}
T::adde(e[i].to,tot);
}
else{
fa[e[i].to] = x, dfs(e[i].to);
if ( !used[e[i].to] ) used[e[i].to] = 1 , T::adde(x,e[i].to);
}
}
}
}
po姐题解
注意环上点是有序的,而树上子树无序。
环有翻转同构,环为根的话还有旋转同构
代码是NEERC 13 C
数据范围大一点
//仙人掌同构数
#include
using namespace std;
#define PB push_back
#define lowbit(x) (x&(-x))
#define MP make_pair
#define fi first
#define se second
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define rep(i,l,r) for (register int i = l ; i <= r ; i++)
#define down(i,r,l) for (register int i = r ; i >= l ; i--)
#define fore(i,x) for (int i = head[x] ; i ; i = e[i].next)
#define SZ(v) (int)v.size()
typedef long long ll;
typedef pair <int,int> pr;
const int maxn = 1e6 + 10;
const int inf = 1e8;
const ll MOD = 180143985094819841ll;
//const ll MOD = 1e9 + 7;
const ll C1 = 35224111;
const ll C2 = 33352437;
const ll C3 = 70525121;
ll mul_C2[maxn],inv_C2[maxn];
int prime[maxn],cnt,tag[maxn],mn_p[maxn];
ll mul(ll a,ll b){
// return a * b % MOD;
return (a * b - (ll)(a / (long double)MOD * b + 1e-3)* MOD + MOD) % MOD;
// return (__int128)a * b % MOD;
}
ll power(ll x,ll y){
ll res = 1;
while ( y ) {
if ( y & 1 ) res = mul(res,x);
x = mul(x,x);
y >>= 1;
}
return res;
}
void init(){
ll inv = power(C2,MOD - 2);
mul_C2[0] = inv_C2[0] = 1;
for (int i = 1 ; i < maxn ; i++){
mul_C2[i] = mul(mul_C2[i - 1],C2);
inv_C2[i] = mul(inv_C2[i - 1],inv);
}
for (int i = 2 ; i < maxn ; i++){
if ( !tag[i] ) prime[++cnt] = i , mn_p[i] = i;
for (int j = 1 ; j <= cnt && prime[j] * i < maxn ; j++){
tag[i * prime[j]] = 1;
mn_p[i * prime[j]] = prime[j];
if ( i % prime[j] == 0 ) break;
}
}
}
map <int,int> ans;
int tot,n,m;
struct node{
int next,to,w;
};
void add_to_ans(int num){
while ( num > 1 ){
int c = 0 , cp = mn_p[num];
while ( num % cp == 0 ) num /= cp , c++;
ans[cp] += c;
}
}
namespace T{
vector <int> e[maxn * 2];
int mx[maxn * 2],sz[maxn * 2],tag[maxn * 2];
pr id[2] = {{inf,0},{inf,0}};
void adde(int x,int y){ //cout<
e[x].PB(y) , e[y].PB(x); }
void find_g(int x,int fa){
sz[x] = 1;
for (auto y : e[x]){
if ( y == fa ) continue;
find_g(y,x);
sz[x] += sz[y];
mx[x] = max(mx[x],sz[y]);
}
}
ll get_hash_tree(vector <ll> &vec){
ll res = 1240198;
for (auto x : vec){
res = (res + power(C1,x)) % MOD;
}
return res;
}
ll get_hash_cyc(vector <ll> &vec){
ll res = 3982003; ll q = 1;
for (auto x : vec){
q = mul(q,C2);
res = (res + mul(q,power(C3,x))) % MOD;
}
return res;
}
vector <ll> all_cycles(vector <ll> &vec){
vector <ll> item(SZ(vec) * 2);
for (int i = 0 ; i < SZ(vec) ; i++){
item[i] = mul(mul_C2[i + 1],power(C3,vec[i]));
}
for (int i = 0 ; i < SZ(vec) ; i++){
item[i + SZ(vec)] = mul(mul_C2[i + 1 + SZ(vec)],power(C3,vec[i]));
}
for (int i = 1 ; i < SZ(item) ; i++) item[i] = (item[i] + item[i - 1]) % MOD;
vector <ll> res(SZ(vec));
for (int i = 0 ; i < SZ(vec) ; i++){
ll d = (item[i + SZ(vec) - 1] - (i ? item[i - 1] : 0) + MOD) % MOD;
d = mul(inv_C2[i],d);
res[i] = d;
}
return res;
}
ll hash_cactus(int x,int fa){
vector <ll> vec;
if ( tag[x] ){
if ( fa == 0 ){
for (auto y : e[x])
vec.PB(hash_cactus(y,x));
vector <ll> h = all_cycles(vec);
int num = 0;
for (auto y : h) if ( y == h[0] ) num++;
reverse(vec.begin(),vec.end());
vector <ll> h2 = all_cycles(vec);
for (auto y : h2) if ( y == h[0] ) num++;
if ( num > 1 ) add_to_ans(num);
return 0;
}
else{
int up = -1;
for (int i = 0 ; i < SZ(e[x]) ; i++){
if ( e[x][i] == fa ){ up = i; break; }
}
assert(up != -1);
for (int i = up + 1 ; i < SZ(e[x]) ; i++){
vec.PB(hash_cactus(e[x][i],x));
}
for (int i = 0 ; i < up ; i++){
vec.PB(hash_cactus(e[x][i],x));
}
ll h1 = get_hash_cyc(vec);
reverse(vec.begin(),vec.end());
ll h2 = get_hash_cyc(vec);
if ( h1 == h2 ) ans[2]++;
return min(h1,h2);
}
}
else{
for (auto y : e[x]){
if ( y == fa ) continue;
vec.PB(hash_cactus(y,x));
}
sort(vec.begin(),vec.end());
int cur = 1;
for (int i = 1 ; i < SZ(vec) ; i++){
if ( vec[i] != vec[i - 1] ) cur = 0;
++cur;
if ( cur > 1 ) add_to_ans(cur);
}
return get_hash_tree(vec);
}
}
void solve(){
find_g(1,0);
for (int i = 1 ; i <= tot ; i++){
int cur = max(mx[i],tot - sz[i]);
if ( id[0].fi > cur ) id[0] = {cur,i} , id[1] = {inf,0};
else if ( id[1].fi > cur ) id[1] = {cur,i};
}
// cout<
if ( id[0].fi == id[1].fi ){
ll h1 = hash_cactus(id[0].se,id[1].se);
ll h2 = hash_cactus(id[1].se,id[0].se);
if ( h1 == h2 ) ans[2]++;
}
else hash_cactus(id[0].se,0);
}
}
namespace G{
node e[maxn * 2];
int head[maxn],cnt,dfn[maxn],fa[maxn],dfstime,used[maxn];
void adde(int x,int y){
// if ( x < y ) cout<
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
void dfs(int x){
dfn[x] = ++dfstime;
for(int i = head[x] ; i ; i = e[i].next){
if ( e[i].to == fa[x] ) continue;
if ( dfn[e[i].to] ){
if ( dfn[e[i].to] > dfn[x] ) continue;
//find_circle
++tot , T::tag[tot] = 1;
int z = x;
while ( z != e[i].to ){
T::adde(tot,z);
used[z] = 1;
z = fa[z];
}
T::adde(e[i].to,tot);
}
else{
fa[e[i].to] = x, dfs(e[i].to);
if ( !used[e[i].to] ) used[e[i].to] = 1 , T::adde(x,e[i].to);
}
}
}
}
int main(){
freopen("cactus.in","r",stdin);
freopen("cactus.out","w",stdout);
// freopen("input.txt","r",stdin);
scanf("%d %d",&n,&m);
for (int i = 1 ; i <= m ; i++){
/* int x,y;
scanf("%d %d",&x,&y);
G::adde(x,y) , G::adde(y,x);*/
int k,x,y = 0;
scanf("%d",&k);
for (int i = 1 ; i <= k ; i++){
scanf("%d",&x);
if ( y ) G::adde(x,y) , G::adde(y,x);
y = x;
}
}
init();
tot = n;
G::dfs(1);
//cout<<"solve\n";
T::solve();
if ( ans.size() == 0 ) puts("0");
else{
printf("%d\n",ans.size());
for (auto y : ans){
printf("%d %d\n",y.fi,y.se);
}
/* ll as = 1 , mod = 1e9 + 3;
for (auto y : ans){
for (int k = 0 ; k < y.se ; k++) as = as * y.fi % mod;
}
cout<
}
}
感觉仙人掌好难造,我是在树上随机加边
#include
using namespace std;
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;
const ld inf = 2e18;
const int N = 3e6 + 10;
const int maxn = 100020;
const ll mod = 1e9 + 7;
inline ll power(ll x,ll y){
y = ((y % (mod - 1)) + (mod - 1)) % (mod - 1);
// if ( y < 0 ) return power(power(x,-y),mod - 2);
ll res = 1;
while ( y ){
if ( y & 1 ) res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
set <pr> s;
vector <int> e[maxn];
int fa[maxn],dth[maxn],tag[maxn];
void adde(int x,int y){
e[x].pb(y);
e[y].pb(x);
}
void dfs(int x){
rvc(i,e[x]){
int to = e[x][i];
if ( to == fa[x] ) continue;
fa[to] = x , dth[to] = dth[x] + 1;
dfs(to);
}
}
bool lca(int x,int y){
int flag = 0;
while ( x != y ){
if ( dth[x] < dth[y] ) swap(x,y);
flag |= tag[x] , x = fa[x];
}
flag |= tag[x]; //只要求边不在多个环上,删掉这句话
return flag ^ 1;
}
void cov(int x,int y){
while ( x != y ){
if ( dth[x] < dth[y] ) swap(x,y);
tag[x] = 1 , x = fa[x];
}
tag[x] = 1; //只要求边不在多个环上,删掉这句话
}
int main(){
freopen("1.cnt","r",stdin);
int cnt;
scanf("%d",&cnt);
fclose(stdin);
freopen("1.cnt","w",stdout);
cout<<++cnt;
fclose(stdout);
srand(cnt + time(0));
freopen("input.txt","w",stdout);
int n = 20,m = n + rand() % 2;
cout<<n<<" "<<m<<endl;
rep(i,1,n) cout<<rand() % 100 + 1<<" ";
cout<<endl;
rep(i,2,n){
int x = rand() % (i - 1) + 1;
adde(i,x);
cout<<i<<" "<<x<<endl;
s.insert(mp(x,i)) , s.insert(mp(i,x));
}
dfs(1);
rep(i,1,m - n + 1){
int x = rand() % n + 1 , y = rand() % n + 1;
while ( x == y || !lca(x,y) || s.find(mp(x,y)) != s.end() ) x = rand() % n + 1 , y = rand() % n + 1;
cov(x,y) , s.insert(mp(x,y)) , s.insert(mp(y,x));
cout<<x<<" "<<y<<endl;
}
}
一道40min就应该ok的圆方树DP。
犯了两个SB错误!
1. 特判方点忘了往下继续dfs
2. 往下dfs但是环上DP用的数组标号,数组被下面的点修改
都是非常简单。但我经常犯的错误!
从训练到下来总共调了1小时20分钟,非常浪费时间!
#include
using namespace std;
#define pb push_back
#define rep(i,l,r) for (int i = l ; i <= r ; i++)
const int maxn = 3e5 + 20;
const int inf = 1e9;
vector <int> e[maxn],e2[maxn];
vector <int> cur;
int tops,n,m,dfn[maxn],low[maxn],tot,tag[maxn],val[maxn],dfstime,st[maxn];
int f[maxn][2],h[maxn][2][2],g[2][2];
void adde(int x,int y){
e[x].pb(y);
e[y].pb(x);
}
void adde2(int x,int y);
void tarjian(int x,int fa){
dfn[x] = low[x] = ++dfstime;
for (int i = 0 ; i < e[x].size() ; i++){
int to = e[x][i];
if ( to == fa ) continue;
if ( !dfn[to] ){
st[++tops] = to;
tarjian(to,x);
low[x] = min(low[to],low[x]);
if ( low[to] >= dfn[x] ){
cur.clear();
do{
cur.pb(st[tops]);
}while ( st[tops--] != to );
if ( cur.size() == 1 ){
adde2(x,cur[0]);
}
else if ( cur.size() > 1 ){
tag[++tot] = 1;
adde2(x,tot);
for (int i = 0 ; i < cur.size() ; i++) adde2(tot,cur[i]);
//for (int i = 0 ; i < cur.size() ; i++) cout<
// cout<<"end circle\n";
}
}
}
else{
low[x] = min(dfn[to],low[x]);
}
}
}
#define e e2
void adde2(int x,int y){
// cout<
e[x].pb(y);
e[y].pb(x);
}
void build(){
tot = n;
for (int i = 1 ; i <= n ; i++) if ( !dfn[i] ) tarjian(i,0);
//for (int i = 1 ; i <= n ; i++) cout<
}
void dfs(int x,int fa){
if ( !tag[x] ){
f[x][0] = 0 , f[x][1] = val[x];
for (int i = 0 ; i < e[x].size() ; i++){
int to = e[x][i];
if ( to == fa ) continue;
dfs(to,x);
if ( !tag[to] ){
int w = max(f[to][1],f[to][0]);
//g[x][0] = f[x][0] , g[x][1] = f[x][1] , f[x][0] = f[x][1] = 0;
f[x][0] += w;
f[x][1] += f[to][0];
}
else{
f[x][0] += f[to][0];
f[x][1] += f[to][1] - val[x];
}
}
return;
}
h[x][0][1] = h[x][1][0] = -inf , h[x][0][0] = 0 , h[x][1][1] = val[e[x][0]];
for (int i = 1 ; i < e[x].size() ; i++){
int to = e[x][i];
dfs(to,x); //忘了往下递归!
memcpy(g,h[x],sizeof(h[x])); //一定要注意递归下去的数组会改变。不能用标号h[i - 1]!!!
h[x][1][1] = g[1][0] + f[to][1];
h[x][1][0] = max(g[1][0],g[1][1]) + f[to][0];
h[x][0][1] = g[0][0] + f[to][1];
h[x][0][0] = max(g[0][0],g[0][1]) + f[to][0];
}
f[x][0] = max(h[x][0][1],h[x][0][0]);
f[x][1] = h[x][1][0];
}
#undef e
int main(){
// freopen("input.txt","r",stdin);
scanf("%d %d",&n,&m);
for (int i = 1 ; i <= n ; i++) scanf("%d",&val[i]);
for (int i = 1 ; i <= m ; i++){
int x,y;
scanf("%d %d",&x,&y);
adde(x,y);
}
build();
dfs(1,0);
// for (int i = 1; i <= tot ; i++) cout<
cout<<max(f[1][0],f[1][1])<<endl;
}
仙人掌上最短路
直接圆方树
注意建树的时候处理环长的 问题。
我利用了仙人掌的性质:每条边只在一个环上,如果有返祖边,最多只有一条,并且一定成环,所以把这条边提前放入栈中。这样算环长很方面。
在圆方树上求lca要讨论是圆点还是方点,如果是方点,则表示在环上合并,要取距离较小的一段
倍增被卡常。悄悄开了O3
#include
using namespace std;
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;
const ld inf = 2e18;
const int N = 3e6 + 10;
const int maxn = 30020;
const ll mod = 1e9 + 7;
vector <int> e[maxn],e2[maxn];
vector <ll> w[maxn],w2[maxn];
vector <pr> cur;
int tops,n,m,Q,dfn[maxn],low[maxn],tot,tag[maxn],val[maxn],dfstime;
pr st[maxn];
int jump[15][maxn],dth[maxn],fa[maxn];
ll len[maxn],dis[15][maxn];
void adde(int x,int y,int d){
e[x].pb(y);
w[x].pb(d);
e[y].pb(x);
w[y].pb(d);
}
void adde2(int x,int y,ll d);
void tarjian(int x,int fa){
dfn[x] = low[x] = ++dfstime;
for (int i = 0 ; i < e[x].size() ; i++){
int to = e[x][i];
if ( to == fa ) continue;
st[++tops] = mp(to,w[x][i]);
if ( !dfn[to] ){
tarjian(to,x);
low[x] = min(low[to],low[x]);
if ( low[to] >= dfn[x] ){
ll tot_L = 0;
cur.clear();
do{
cur.pb(st[tops]);
tot_L += st[tops].se;
}while ( st[tops--].fi != to );
if ( cur.size() == 1 ){
adde2(x,cur[0].fi,cur[0].se);
}
else if ( cur.size() > 1 ){
tag[++tot] = 1 , len[tot] = tot_L;
adde2(x,tot,0);
ll curd = 0;
for (int i = 0 ; i < cur.size() ; i++){
//cout<
len[cur[i].fi] = curd;
if ( i ) adde2(tot,cur[i].fi,min(tot_L - curd,curd));
curd += cur[i].se;
}
//cout<<"end circle\n";
}
}
}
else{
//low[x] = min(dfn[to],low[x]);
if ( dfn[to] < dfn[x] ) low[x] = dfn[to];
else tops--;
}
}
}
#define e e2
#define w w2
void adde2(int x,int y,ll d){
//cout<
e[x].pb(y);
w[x].pb(d);
e[y].pb(x);
w[y].pb(d);
}
void build(){
tot = n;
for (int i = 1 ; i <= n ; i++) if ( !dfn[i] ) tarjian(i,0);
//for (int i = 1 ; i <= n ; i++) cout<
}
void dfs(int x){
//cout<<"dfs : "<
for (int i = 0 ; i < e[x].size() ; i++){
int to = e[x][i];
if ( to == fa[x] || dth[to] ) continue;
fa[to] = jump[0][to] = x;
dth[to] = dth[x] + 1 , dis[0][to] = w[x][i];
//cout<
dfs(to);
}
}
void init(){
// rep(i,1,tot) cout<
// cout<
//for (int i = 1 ; i <= n ; i++){ rvc(j,e[i]) cout<
dth[1] = 1 , dfs(1);
rep(i,1,13)
rep(j,1,tot)
jump[i][j] = jump[i - 1][jump[i - 1][j]] , dis[i][j] = dis[i - 1][j] + dis[i - 1][jump[i - 1][j]];
// rep(i,1,tot){ cout<<"check : "<
}
inline int lca(int &x,int &y,ll &res){
if ( dth[x] < dth[y] ) swap(x,y);
int d = dth[x] - dth[y];
rep(i,0,13) if ( (1 << i) & d ) res += dis[i][x], x = jump[i][x];
if ( x == y ) return x;
repd(i,13,0) if ( jump[i][x] != jump[i][y] ) res += dis[i][x] + dis[i][y] , x = jump[i][x] , y = jump[i][y];
//res += dis[0][x] + dis[0][y];
return fa[x];
}
ll query(int x,int y){
ll res = 0;
int d = lca(x,y,res);
//cout<
if ( tag[d] ){
res += min(abs(len[x] - len[y]) , len[d] - abs(len[x] - len[y]));
}
else if ( x != y ) res += dis[0][x] + dis[0][y];
return res;
}
#undef e
#undef w
int main(){
//
// freopen("input.txt","r",stdin);
scanf("%d %d %d",&n,&m,&Q);
rep(i,1,m){
int x,y,w;
scanf("%d %d %d",&x,&y,&w);
adde(x,y,w);
//adde(y,x,w);
}
build();
init();
while ( Q-- ){
int x,y;
scanf("%d %d",&x,&y);
printf("%lld\n",query(x,y));
}
}