bzoj 5206 [Jsoi2017]原力

时间:2020-03-23
本文章向大家介绍bzoj 5206 [Jsoi2017]原力,主要包括bzoj 5206 [Jsoi2017]原力使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

LINK:原力

一张无向图 这道题统计三元环的价值和。有重边但是无自环。

我曾经写过三元环计数 这个和那个题差不太多。

不过有很多额外操作 对于重边问题 我们把所有颜色相同的重边缩在一起 这样的话我们就可以针对点来运算了。

不过这样做开邻接表就很困难了 (直接map爽...

接下来是正规的三元环计数了 我们考虑重新建图 度数小的连向度数大的 度数相等的由编号小的连向编号大的。

可以暴力枚举每个点 标记所到达的点(暴力枚举合并过后的边 再枚举一条边去统计答案 这样就完事了。

可以观察一下复杂度 外层暴力枚举是O(m)的 内层我们考虑再枚举一条边 度数小于sqrt(m)的点复杂度为sqrt(m)

大于sqrt(m)的因为连向的只有>sqrt(m)的所以复杂度还是sqrt(m).

那其实内层均摊msqrt(m) 外层枚举好像跟内层复杂度没有联系 所以总复杂度 msqrt(m).

这里简述另外一种分治法:(这种分治法更容易分析复杂度。

对于三个点度数大于sqrt(m)的 我们可以直接暴力枚举三个点 复杂度显然msqrt(m).

反之 枚举一个点 其两条出边来判断 不重不漏的话我们规定编号最小的点能够枚举到这个环即可。

可以看出后者复杂度 msqrt(m).

对于这两种分治法 一般叫做根号分治 非常行之有效的方法。

运用第一种方法 我T的不省人事。

const int MAXN=100010;
int n,m,len;
struct wy{int x,y;}t[MAXN];
int ru[MAXN];
map<pii,int>H[3];
char a[10];
int lin[MAXN],ver[MAXN],nex[MAXN];
inline void add(int x,int y){ver[++len]=y;nex[len]=lin[x];lin[x]=len;}
int main()
{
    //freopen("1.in","r",stdin);
    gt(n);gt(m);
    rep(1,m,i)
    {
        int x,y,z;
        gt(x);gt(y);gt(z);gc(a);
        int w=a[1]=='R'?0:a[1]=='G'?1:2;
        if(x>y)swap(x,y);++ru[x];++ru[y];
        H[w][mk(x,y)]=(H[w][mk(x,y)]+z)%mod;
        t[i]=(wy){x,y};
    }
    for(int i=1;i<=m;++i)
    {
        int x=t[i].x,y=t[i].y;
        if(ru[x]==ru[y]||ru[x]<ru[y])add(x,y);
        else add(y,x);
    }
    ll ans=0;
    for(int i=1;i<=n;++i)
    {
        for(int j=lin[i];j;j=nex[j])
        {
            int tn=ver[j];
            for(int k=lin[tn];k;k=nex[k])
            {
                int w1=ver[k];
                ans=(ans+(ll)H[1][mk(min(i,tn),max(i,tn))]*H[2][mk(min(tn,w1),max(tn,w1))]*H[0][mk(min(i,w1),max(i,w1))])%mod;
                ans=(ans+(ll)H[1][mk(min(i,tn),max(i,tn))]*H[0][mk(min(tn,w1),max(tn,w1))]*H[2][mk(min(i,w1),max(i,w1))])%mod;
                ans=(ans+(ll)H[2][mk(min(i,tn),max(i,tn))]*H[1][mk(min(tn,w1),max(tn,w1))]*H[0][mk(min(i,w1),max(i,w1))])%mod;
                ans=(ans+(ll)H[2][mk(min(i,tn),max(i,tn))]*H[0][mk(min(tn,w1),max(tn,w1))]*H[1][mk(min(i,w1),max(i,w1))])%mod;
                ans=(ans+(ll)H[0][mk(min(i,tn),max(i,tn))]*H[1][mk(min(tn,w1),max(tn,w1))]*H[2][mk(min(i,w1),max(i,w1))])%mod;
                ans=(ans+(ll)H[0][mk(min(i,tn),max(i,tn))]*H[2][mk(min(tn,w1),max(tn,w1))]*H[1][mk(min(i,w1),max(i,w1))])%mod;
            }
        }
    }
    putl(ans);
    //cout<<H[1][mk(min(1,2),max(1,2))]<<endl;
    return 0;
}
```
这样做常数什么的极大。

10s可能连一个点都跑不过(本地测试。

这里优化方法是 好像可以附上边权会好很多 就不需要一次判六次了。

我手写了hash 结果还是歇菜。

const int MAXN=100010,M=10000007;
int n,m,len,len1;
struct wy{int x,y,z,e;}t[MAXN];
int ru[MAXN];char a[10];
struct jl
{
int x,y,w;
jl(){x=y=w=0;};
jl(int a,int b,int c){x=a;y=b;w=c;}
inline int friend operator (jl a,jl b){return a.xb.x&&a.yb.y&&a.wb.w;}
}s[M],s1;
int lin[MAXN],ver[MAXN],nex[MAXN],e[MAXN],e1[MAXN];
inline void add(int x,int y,int z,int z1){ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z;e1[len]=z1;}
int lin1[M],nex1[M],v[M];
inline int ha1(int x,int y,int w){return ((xP+w)%MP+y+wy%Mx)P%M;}
inline int find(int x,int y,int w,int z)
{
s1=jl(x,y,w);
int xx=ha1(x,y,w);
for(int i=lin1[xx];i;i=nex1[i])
if(s1==s[i]){v[i]=(v[i]+z)%mod;return v[i];}
return 0;
}
inline void ha(int x,int y,int w,int z)
{
if(find(x,y,w,z))return;
int ww=((x
P+w)%MP+y+wy%M*x)*P%M;
s[++len1]=s1;
nex1[len1]=lin1[ww];
lin1[ww]=len1;
v[len1]=z;
}
int main()
{
freopen("1.in","r",stdin);
gt(n);gt(m);
rep(1,m,i)
{
int x,y,z;
gt(x);gt(y);gt(z);gc(a);
int w=a[1]'R'?0:a[1]'G'?1:2;
if(x>y)swap(x,y);++ru[x];++ru[y];
t[i]=(wy){x,y,w,z};ha(x,y,w,z);
}
for(int i=1;i<=m;++i)
{
int x=t[i].x,y=t[i].y,w=t[i].z,z=t[i].e;
if(ru[x]==ru[y]||ru[x]<ru[y])add(x,y,w,z);
else add(y,x,w,z);
}
ll ans=0;
for(int i=1;i<=n;++i)
{
for(int j=lin[i];j;j=nex[j])
{
for(int k=lin[ver[j]];k;k=nex[k])
{
int w1=ver[k];
if(e[k]==e[j])continue;
int w2=i;if(w1>w2)swap(w1,w2);
int ww=find(w1,w2,3-e[j]-e[k],0);
ans=(ans+(ll)e1[j]e1[k]%modww)%mod;
}
}
}
putl(ans);
return 0;
}

我hash写丑了么???
我也不知道为什么。
计出下策 采用第二种方法写。

原文地址:https://www.cnblogs.com/chdy/p/12555061.html