L_0_Forever_LF的专栏

一个热爱OI的OIer

5.30联考题解

A. 给出n个m维空间中的点,对于点A(x0,x1....xm1),B(y0,y1....ym1) (0<=xi,yi<4),两点的距离定义为i=0m1|xiyi|,求距离为0~3m中每个值的点对数量

粗略的写一下题解,具体的可以参见 2017国家集训队论文海蜇?海蜇!解题报告步骤二部分

我们可以将每个点看作一个m位的四进制数

我们定义AB=C,C(|x0y0|,|x1y1|.....|xm1ym1|)(同样对点转换成的四进制数a,b也这样定义ab,可以发现如果在二进制下这样定义就是异或)
h[i]表示有多少个点转化成四进制为i
再定义对于数组h的乘法,a=fga[k]=i=04m1j=04m1[ij=k]f[i]g[j]

那么我们只要求出a=hh,就能求出距离为0~3m中每个值的点对数量

我们令ai表示数组a中四进制最高位为i的部分,有
a0=f0g0+f1g1+f2g2+f3g3
a1=f0g1+f1g0+f1g2+f2g1+f2g3+f3g2
a2=f0g2+f1g3+f2g0+f3g1
a3=f0g3+f3g0

我们可以类似fwt的过程,用f0+f1+f2+f3f0f1+f2f3f0+f3f0f3等优化数组之间乘的次数,把16次优化到6次,然后分治下去处理

复杂度O(6m)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 1<<18;

void fwt(ll a[],ll f[],ll g[],int N)
{
    if(N==1) { a[0]=f[0]*g[0];return; }
    int n=N/4;
    ll u[n],v[n],ft[n],gt[n];

    for(int i=0;i<n;i++) ft[i]=f[i]+f[i+n]+f[i+2*n]+f[i+3*n];
    for(int i=0;i<n;i++) gt[i]=g[i]+g[i+n]+g[i+2*n]+g[i+3*n];
    fwt(u,ft,gt,n);

    for(int i=0;i<n;i++) ft[i]=f[i]-f[i+n]+f[i+2*n]-f[i+3*n];
    for(int i=0;i<n;i++) gt[i]=g[i]-g[i+n]+g[i+2*n]-g[i+3*n];
    fwt(v,ft,gt,n);

    for(int i=0;i<n;i++) a[2*n+i]=(u[i]+v[i])>>1,a[n+i]=(u[i]-v[i])>>1;

    for(int i=0;i<n;i++) ft[i]=f[i]+f[i+3*n];
    for(int i=0;i<n;i++) gt[i]=g[i]+g[i+3*n];
    fwt(u,ft,gt,n);

    for(int i=0;i<n;i++) ft[i]=f[i]-f[i+3*n];
    for(int i=0;i<n;i++) gt[i]=g[i]-g[i+3*n];
    fwt(v,ft,gt,n);

    for(int i=0;i<n;i++)
    {
        a[3*n+i]=(u[i]-v[i])>>1;
        a[n+i]-=a[3*n+i];
    }
    for(int i=0;i<n;i++) a[i]=(u[i]+v[i])>>1;

    for(int i=0;i<n;i++) ft[i]=f[n+i];
    for(int i=0;i<n;i++) gt[i]=g[n+i];
    fwt(u,ft,gt,n);

    for(int i=0;i<n;i++) a[i]+=u[i];

    for(int i=0;i<n;i++) ft[i]=f[2*n+i];
    for(int i=0;i<n;i++) gt[i]=g[2*n+i];
    fwt(v,ft,gt,n);

    for(int i=0;i<n;i++)
    {
        a[i]+=v[i];
        a[2*n+i]-=a[i];
    }
}
int n,m;
ll a[maxn],f[maxn],g[maxn];
ll ans[maxn];

int main()
{
    freopen("space.in","r",stdin);
    freopen("space.out","w",stdout);

    read(n); read(m);
    for(int i=1;i<=n;i++)
    {
        int now=0;
        for(int j=0;j<m;j++)
        {
            int x; read(x);
            now|=x<<j*2;
        }
        f[now]++,g[now]++;
    }
    fwt(a,f,g,1<<m*2);

    for(int i=0;i<(1<<m*2);i++)
    {
        int dis=0;
        for(int j=0;j<m;j++) dis+=i>>j*2&3;
        ans[dis]+=a[i];
    }
    for(int i=0;i<=3*m;i++) printf("%lld%c",ans[i],i!=3*m?' ':'\n');

    return 0;
}

B.
考虑对每条边分开计算他贡献的概率

模拟Kruskal的过程,从小到大处理边
f[i][mask]表示处理到第i条边,前i1条边已经确定了是否存在,mask是一个极大联通块的概率(这里极大联通块的定义为不能在往点集中添加点使得这些点仍然两辆联通)
对于第i条边(u,v),他贡献的概率是f[i][j]juv

考虑转移
j不包含u,vf[i+1][j]=f[i][j],这条边是否存在与这个联通块无关
j包含u,v中的一个,这条边一定不存在,f[i+1][j]=f[i][j]pi

j同时包含u,v,枚举j的子集kf[i+1][j]=f[i][j]+k,kuv     f[i][k]f[i][jk](1pi)/t
其中t表示k,jk共有的边不存在的概率,因为这部分概率会被k,jk各计算一次,要除回来,计算t可以用h[mask]表示点集mask内所有边不存在的概率,t=h[j]h[k]h[jk],因为我比较zz,考试时没想到这个,写了个分段打表的东西,带个m20的系数计算t,但因为状态总数不是满的所以也能跑过去

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 14;
const int maxm = 95;
const int mask2= 1<<14;
const int mask3= 4782969;
const double eps = 1e-15;

int to3[mask2];

int n,m,al,el=(1<<20)-1;
struct edge
{
    int x,y,c;
    double pi;
    friend inline bool operator <(const edge x,const edge y){return x.c<y.c;}
}e[maxm];
double pe[5][1<<20];
struct data
{
    int ei[5];
    void add(int i) { ei[i/20]|=1<<(i%20); }
    friend inline data operator &(const data &a,const data &b)
    {
        data re;
        for(int i=0;i<5;i++) re.ei[i]=a.ei[i]&b.ei[i];
        return re;
    }
    double cal()
    {
        double r=1.0;
        for(int i=0;i<5;i++) r=r*pe[i][ei[i]];
        return r;
    }
}ei[mask2],now;

double ans;
double g[maxm][mask2];

int main()
{
    freopen("bridge.in","r",stdin);
    freopen("bridge.out","w",stdout);

    scanf("%d%d",&n,&m); al=(1<<n)-1;

    for(int i=0;i<=al;i++) to3[i]=to3[i>>1]*3+(i&1);
    for(int i=0;i<5;i++) for(int j=0;j<=el;j++) pe[i][j]=1.0;
    for(int i=0;i<m;i++)
    {
        scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].c); e[i].x--,e[i].y--;
        scanf("%lf",&e[i].pi);
        if(e[i].x==e[i].y) { i--;m--;continue; }
    }
    sort(e,e+m);
    for(int i=0;i<m;i++)
    {
        for(int S=al^(1<<e[i].x),s=S;;s=(s-1)&S)
        {
            ei[s|1<<e[i].x].add(i);
            if(!s)break;
        }
        for(int S=al^(1<<e[i].y),s=S;;s=(s-1)&S)
        {
            ei[s|1<<e[i].y].add(i);
            if(!s)break;
        }

        for(int t=i/20,j=i%20,S=el^(1<<j),s=S;;s=(s-1)&S)
        {
            pe[t][s|1<<j]*=e[i].pi;
            if(!s)break;
        }
    }

    for(int i=0;i<n;i++) g[0][1<<i]=1.0;
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<=al;j++) if((j>>e[i].x&1)&&!(j>>e[i].y&1)&&g[i][j]>eps)
            ans+=g[i][j]*(1-e[i].pi)*e[i].c;

        for(int j=0;j<=al;j++) 
        {
            if(!(j>>e[i].x&1)&&!(j>>e[i].y&1)) g[i+1][j]=g[i][j];
            else if((j>>e[i].x&1)^(j>>e[i].y&1)) g[i+1][j]=g[i][j]*e[i].pi;
            else
            {
                g[i+1][j]=g[i][j];
                for(int S=j^(1<<e[i].x)^(1<<e[i].y),s=S;;s=(s-1)&S)
                {
                    int x=s|1<<e[i].x,y=(S^s)|1<<e[i].y;
                    if(g[i][x]>eps&&g[i][y]>eps)
                    {
                        double tc=(ei[x]&ei[y]&now).cal();
                        if(tc>eps) g[i+1][j]+=g[i][x]*g[i][y]/tc*(1-e[i].pi);
                    }
                    if(!s)break;
                }
            }
        }
        now.add(i);
    }

    printf("%.6lf\n",ans);

    return 0;
}

C.
朴素的想法是直接跑费用流
考虑一些优化
源连白点流量1,黑点连汇流量1,把黑白点排列在数轴上,相邻点连边流量inf费用距离,这样可以把边数降到O(n)
因为费用流的特性,每次流必定是在剩下的点中选一对相邻的黑白点流,然后去掉这两个点。我们可以用数据结构代替费用流去跑这个东西,相邻的每对点维护两个vector表示两个方向反向边的费用和,按流量从大到小排,把不同色的相邻点放堆里,每次取出堆顶跑,然后将他和相邻的两条边合并,合并用vector的启发式合并

复杂度O(nlogn)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define SZ(x) ((int)x.size())
#define End(x) (x.begin()+SZ(x)-1)
using namespace std;

inline void read(int &x)
{
    int f=1; char c; 
    while(!((c=getchar())>='0'&&c<='9')) if(c=='-') f=-1;
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
    if(f==-1) x=-x;
}
const int maxn = 410000;

ll ans;
int n,m,K;
pair<int,int>xi[maxn];
int dis[maxn];
struct List
{
    int fa[maxn];
    void init(){for(int i=1;i<=n;i++) fa[i]=i;}
    int findfa(const int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}
}L,R;

int vi[maxn][2],nowd[maxn];
vector<int>V[maxn<<1];

int qend(int x,int dir)
{
    int ix=vi[x][dir];
    return SZ(V[ix])?(*End(V[ix])):0;
}
void Run(int x,int dir)
{
    int ix=vi[x][dir],ix2=vi[x][!dir];
    int o=0;
    if(SZ(V[ix])) o=(*End(V[ix])),V[ix].erase(End(V[ix]));
    ans+=nowd[x]-o*2;
    V[ix2].pb(nowd[x]-o);
}
void merge(int x,int y)
{
    nowd[x]+=nowd[y];

    int sx,sy;
    int &ix0=vi[x][0],&iy0=vi[y][0]; if(SZ(V[ix0])<SZ(V[iy0])) swap(ix0,iy0);
    sx=SZ(V[ix0]),sy=SZ(V[iy0]);
    for(int i=0;i<sy;i++) V[ix0][sx-(sy-i)]+=V[iy0][i];

    int &ix1=vi[x][1],&iy1=vi[y][1]; if(SZ(V[ix1])<SZ(V[iy1])) swap(ix1,iy1);
    sx=SZ(V[ix1]),sy=SZ(V[iy1]);
    for(int i=0;i<sy;i++) V[ix1][sx-(sy-i)]+=V[iy1][i];
}

set< pair<int,int> >S;
set< pair<int,int> >::iterator it;

int main()
{
    freopen("friend.in","r",stdin);
    freopen("friend.out","w",stdout);

    read(n); read(m); read(K);
    for(int i=1;i<=n;i++) read(xi[i].fir),xi[i].sec=0;
    for(int i=1;i<=m;i++) read(xi[n+i].fir),xi[n+i].sec=1;
    n+=m;
    sort(xi+1,xi+n+1);

    L.init(); R.init();
    for(int i=2;i<=n;i++)
    {
        vi[i][0]=i,vi[i][1]=n+i;
        nowd[i]=xi[i].fir-xi[i-1].fir;
        dis[i]=dis[i-1]+nowd[i];
        if(xi[i-1].sec^xi[i].sec) S.insert(mp(nowd[i],i));
    }

    while(K--)
    {
        it=S.begin(); pair<int,int>temp=(*it); S.erase(it);
        int x=temp.sec,y=L.findfa(x-1),dir=!xi[y].sec;
        if(R.findfa(x)!=x||!y||nowd[x]-qend(x,dir)*2!=temp.fir) {K++;continue;}
        Run(x,dir);

        L.fa[y]=y-1,R.fa[y]=y+1;
        L.fa[x]=x-1,R.fa[x]=x+1;
        if(!L.findfa(y-1)||!R.findfa(x+1)) continue;

        merge(x,y);
        y=R.findfa(x+1); 
        merge(y,x),x=y;

        y=L.findfa(x-1),dir=!xi[y].sec;
        if(xi[x].sec^xi[y].sec) S.insert(mp(nowd[x]-qend(x,dir)*2,x));
    }
    printf("%lld\n",ans);

    return 0;
}
阅读更多
版权声明:...............转载说一声并注明出处qaq............... https://blog.csdn.net/L_0_Forever_LF/article/details/80535183
个人分类: 乱七八糟的东西
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭