TC SRM633

第三题:n个数字,有m个限制,每个限制给出某两个数字的Gcd或者Lcm为多少。最后问这样的n个数存在否。

思路:我们发现,对于素数p1,p2,n个数中每个数含有多少个p1,p2是没有联系的,因此每个素数可以分开考虑。

现在我们计算对于某一个素数p是否存在矛盾,用Min[i],Max[i]计算n个数字中每个数字最少最多含有多少个p。然后第x个数字含有的p要么取Min[x],要么取 Max[x],因此可用2sat解决。

const int N=1005;



#define VI vector<int>

#define YES "Solution exists"

#define NO "Solution does not exist"





vector<int> prime;

int n,m;

vector<int> a,b,c;

string type;





int low[N],dfn[N],visit[N],belong[N],id,color;

stack<int> S;



struct node

{

    int v,next;

};



node edges[N<<1];

int head[N],e;



void add(int u,int v)

{

    edges[e].v=v;

    edges[e].next=head[u];

    head[u]=e++;

}



int Min[N],Max[N];



void upMin(int &x,int y)

{

    if(x>y) x=y;

}



void upMax(int &x,int y)

{

    if(x<y) x=y;

}





void DFS(int u)

{

    dfn[u]=low[u]=++id;

    S.push(u);



    int i;

    for(i=head[u];i!=-1;i=edges[i].next)

    {

        int v=edges[i].v;

        if(!dfn[v])

        {

            DFS(v);

            upMin(low[u],low[v]);

        }

        else if(!visit[v])

        {

            upMin(low[u],dfn[v]);

        }

    }

    if(low[u]==dfn[u])

    {

        color++;

        int v;

        do

        {

            v=S.top(); S.pop();

            visit[v]=1;

            belong[v]=color;

        }while(v!=u);

    }

}



int ok(int x)

{

    int i,j;

    clr(head,-1); e=0;

    clr(dfn,0);

    clr(visit,0);

    id=color=0;

    while(!S.empty()) S.pop();





    for(i=0;i<n;i++) Min[i]=0,Max[i]=1000;



    int num[N];



    for(i=0;i<m;i++)

    {

        int cnt=0;

        int tmp=c[i];

        while(tmp%x==0) cnt++,tmp/=x;

        num[i]=cnt;

        if(type[i]=='G') upMax(Min[a[i]],cnt),upMax(Min[b[i]],cnt);

        else upMin(Max[a[i]],cnt),upMin(Max[b[i]],cnt);

    }

    for(i=0;i<n;i++) if(Min[i]>Max[i]) return 0;

    for(i=0;i<m;i++)

    {

        int u=a[i];

        int v=b[i];

        if(type[i]=='G')

        {

            int x=Min[u]>num[i];

            int y=Min[v]>num[i];

            if(x&&y) return 0;

            else if(x)

            {

                if(Min[v]!=Max[v]) add(v+n,v);

            }

            else if(y)

            {

                if(Min[u]!=Max[u]) add(u+n,u);

            }

            else

            {

                if(Min[v]!=Max[v]&&Min[u]!=Max[u]) add(v+n,u),add(u+n,v);

            }

        }

        else

        {

            int x=Max[u]<num[i];

            int y=Max[v]<num[i];

            if(x&&y) return 0;

            else if(x)

            {

                if(Min[v]!=Max[v]) add(v,v+n);

            }

            else if(y)

            {

                if(Min[u]!=Max[u]) add(u,u+n);

            }

            else

            {

                if(Min[v]!=Max[v]&&Min[u]!=Max[u]) add(v,u+n),add(u,v+n);

            }

        }

    }

    for(i=0;i<n+n;i++) if(!dfn[i]) DFS(i);

    for(i=0;i<n;i++) if(belong[i]==belong[i+n]) return 0;

    return 1;

}





string cal(int _n,string _type,VI _a,VI _b,VI _c)

{

    n=_n;

    type=_type;

    a=_a;

    b=_b;

    c=_c;

    m=SZ(type);

    int i;

    for(i=0;i<m;i++)

    {

        int t=c[i];

        int j;

        for(j=2;j*j<=t;j++) if(t%j==0)

        {

            prime.pb(j);

            while(t%j==0) t/=j;

        }

        if(t>1) prime.pb(t);

    }

    for(i=0;i<SZ(prime);i++)

    {

        if(!ok(prime[i])) return NO;

    }

    return YES;

}



class GCDLCM

{

    public:

    string possible(int n,string type,vector<int> A,vector<int> B,vector<int> C)

    {

        return cal(n,type,A,B,C);

    }

};

 第二题:给出两棵树均含有n个节点(编号0到n-1),每个节点有权值(有正有负)。两个树编号相同的节点的权值相同。找到一个[0,n-1]的子集S,使得S中的节点在两个树中都正好是一个子树。在这种情况下使得S中节点的权值和最大。

思路:对于某个节点u,假如我们最后一定选择该节点,那么对于节点p,我们若选它,那么p到u路径上的点都必须选,这样就成了一个最大权闭合图问题。

const int N=20005;



struct node

{

    int v,next,cap;

};





node edges[N*100];

int head[N],e;

int pre[N],curedge[N],h[N],num[N];

int s,t;





void add(int u,int v,int cap)

{

    edges[e].v=v;

    edges[e].cap=cap;

    edges[e].next=head[u];

    head[u]=e++;

}





void Add(int u,int v,int cap)

{

    add(u,v,cap);

    add(v,u,0);

}





int visit[N];



int Maxflow(int s,int t,int n)

{

    clr(h,0); clr(num,0);

    int i;

    for(i=0;i<n;i++) curedge[i]=head[i];

    int u=s,Min,k,x,ans=0;

    while(h[u]<n)

    {

        if(u==t)

        {

            Min=INF+1;

            for(i=s;i!=t;i=edges[curedge[i]].v)

            {

                x=curedge[i];

                if(edges[x].cap<Min)

                {

                    Min=edges[x].cap;

                    k=i;

                }

            }

            ans+=Min; u=k;

            for(i=s;i!=t;i=edges[curedge[i]].v)

            {

                x=curedge[i];

                edges[x].cap-=Min;

                edges[x^1].cap+=Min;

            }

        }

        for(i=curedge[u];i!=-1;i=edges[i].next)

        {

            if(edges[i].cap>0&&h[u]==h[edges[i].v]+1)

            {

                break;

            }

        }

        if(i!=-1)

        {

            curedge[u]=i;

            pre[edges[i].v]=u;

            u=edges[i].v;

        }

        else

        {

            if(--num[h[u]]==0) break;

            curedge[u]=head[u];

            x=n;

            for(i=head[u];i!=-1;i=edges[i].next)

            {

                k=edges[i].v;

                if(edges[i].cap>0&&h[k]<x) x=h[k];

            }

            h[u]=x+1; num[x+1]++;

            if(u!=s) u=pre[u];

        }

    }

    return ans;

}





#define VI vector<int>



int fa[55];



vector<int> g1[55],g2[55];

int n;

int ss[55];



int f1[55],f2[55];



void DFS(int u,int pre,int fa[],VI g[])

{

    fa[u]=pre;

    int i;

    for(i=0;i<SZ(g[u]);i++)

    {

        int v=g[u][i];

        if(v!=pre) DFS(v,u,fa,g);

    }

}



int cal(int r)

{

    DFS(r,-1,f1,g1);

    DFS(r,-1,f2,g2);

    clr(head,-1); e=0;

    int S=n,T=n+1;

    int sum=0;

    int i;

    for(i=0;i<n;i++)

    {

        if(ss[i]>0) sum+=ss[i],Add(S,i,ss[i]);

        else Add(i,T,-ss[i]);

        if(f1[i]!=-1) Add(i,f1[i],INF);

        if(f2[i]!=-1) Add(i,f2[i],INF);

    }

    sum-=Maxflow(S,T,n+3);

    return sum;

}



int cal(VI a,VI b,VI c,VI d,VI score)

{

    n=SZ(a)+1;

    int i;

    for(i=0;i<n-1;i++)

    {

        int u=a[i],v=b[i];

        g1[u].pb(v);

        g1[v].pb(u);



        u=c[i];

        v=d[i];

        g2[u].pb(v);

        g2[v].pb(u);

    }

    for(i=0;i<n;i++) ss[i]=score[i];

    int ans=0;

    for(i=0;i<n;i++)

    {

        int tmp=cal(i);

        if(tmp>ans) ans=tmp;

    }

    return ans;

}



class DoubleTree

{

    public:

    int maximalScore(vector <int> a,vector<int> b,vector<int> c, vector<int> d, vector<int> score)

    {

        return cal(a,b,c,d,score);

    }

};

 第一题:一个人初始时在(0,0),要跳到(x,0)。给出一个数组,比如(2,4,9),那么跳的长度依次是2,4,9,2,4,9,…… 问最少跳多少次可以到达终点。

思路:两个循环之内可以跳完,那么我们只要让这些步数之内的数字组成两个p,q,使得p,q,x组成三角形即可(p+x=q或者p+q=x都行)。否则,若x是所有数字之和的很多倍,则一开始是一直直着向前跳m次,剩下rex=x-sum*m(sum是数组的数字之和),最后用前t项和组成大于rex的即可(这里大于也无所谓,因为这个一定可以和前面的m*sum以及x组成三角形)。

int cal(int L,vector<int> V)

{

    if(L<0) L=-L;

    if(L==0) return 0;

    int n=SZ(V);

    int i;

    for(i=0;i<n;i++) V.pb(V[i]);

    n<<=1;

    i64 sum=0,Max=0,Min;

    for(i=0;i<n;i++)

    {

        if(V[i]>Max) Max=V[i];

        sum+=V[i];

        if(sum-Max>=Max) Min=0;

        else Min=Max-(sum-Max);

        if(Min<=L&&L<=sum) return i+1;

    }

    int ans=L/sum*n;

    int re=L%sum;

    i=0;

    while(re>0) re-=V[i++],ans++;

    return ans;

}



class PeriodicJumping

{

    public:

    int minimalTime(int x,vector <int> jumpLengths)

    {

        return cal(x,jumpLengths);

    }

};

 

你可能感兴趣的:(rm)