2017暑假集训 div1 并查集(2)

时间:2022-12-20 12:45:44

POJ  1984

题意:有多个点,在平面上位于坐标点上,给出一些关系,表示某个点在某个点的正东/西/南/北方向多少距离,然后给出一系列询问,表示在第几个关系给出后询问某两点的曼哈顿距离,或者未知则输出-1。

做法:x,y左边分别两个权值做权值并查集后,对所有询问强制离线,按询问时间戳排序,处理答案即可。(类似莫队之类的)


#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
using namespace std;
int pre[40100];
int xsum[40100];
int ysum[40100];
int n,m,k;
int ans[40100];

struct node
{
    int x,y;
    int dx,dy;
}edge[40010];

struct enode
{
    int x,y;
    int id;
    int pos;
}q[40010];

bool cmp(struct enode a, struct enode b)
{
    return a.id<b.id;
}
int f(int x)
{
   if(x==pre[x]) return pre[x];
   int fx=pre[x];
   pre[x]=f(pre[x]);
   xsum[x]+=xsum[fx]; ysum[x]+=ysum[fx];
   return pre[x];
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        pre[i]=i; xsum[i]=0; ysum[i]=0;
    }
    char s;
    for(int i=1;i<=m;++i)
    {
        int d,dx,dy;
        scanf("%d %d %d %c",&edge[i].x,&edge[i].y,&d,&s) ;
        if(s=='N')       {dx=0,  dy=d;}
        else if(s=='E')  {dx=d,  dy=0;}
        else if(s=='S')  {dx=0,  dy=-d;}
        else if(s=='W')  {dx=-d, dy=0;}
        edge[i].dx=dx; edge[i].dy=dy;
    }
    scanf("%d",&k);
    for(int i=1;i<=k;++i)
    {
        scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].id);
        q[i].pos=i;
    }
    sort(q+1,q+1+k,cmp);
    int now=0;

    for(int i=1;i<=k;++i)
    {
        while(now!=q[i].id)
        {
            now+=1;
            if(now>m) break;
            int lx=edge[now].x,ly=edge[now].y;
            int flx=f(lx), fly=f(ly);

            pre[fly]=flx;
            xsum[fly]=xsum[lx]-xsum[ly]-edge[now].dx;
            ysum[fly]=ysum[lx]-ysum[ly]-edge[now].dy;
        }

        int qx=q[i].x,qy=q[i].y;
        if(f(qx)!=f(qy))
        {
            ans[q[i].pos]=-1;
        }
        else
        {
            ans[q[i].pos]=abs(xsum[qx]-xsum[qy])+abs(ysum[qx]-ysum[qy]);
        }
    }
    for(int i=1;i<=k;++i)
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}

POJ 1414

题意:给出p1+p2个人,其中p1个是好人,p2个是坏人。然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人。其中比较重要的是,好人总说真话,坏人总说假话。不需要判断矛盾。唯一解

注意:如果说yes 那么两个人一定同类,no不同类

做法:先用关系并查集把所有人分成若干个以某个人为首的大集合,其中包含两个小集合 第一个表示与根节点同类,第二个表示与根节点异类。然后dp dp[i][j] 表示前i 个大集合 好人有j的情况数。(同时记录路径),如果最后一个集合的p1个人只有一种那么说明有答案。(也可以用母函数枚举根节点是好人还是坏人)


#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
const int maxn=630;
int q,p1,p2;
int pre[maxn];
int sum[maxn];

int dp[maxn][maxn];
int path[maxn][maxn];
int num[maxn][2];
vector<int> who[maxn][2];
bool vis[maxn];

int f(int x)
{
    if(x==pre[x]) return pre[x];
    int fx=pre[x];
    pre[x]=f(pre[x]);
    sum[x]=(sum[x]+sum[fx])%2;
    return pre[x];
}

int main()
{
    while(scanf("%d%d%d",&q,&p1,&p2)!=EOF)
    {
        if(q==0&&p1==0&&p2==0) break;
        for(int i=0;i<maxn;++i)  { sum[i] = 0; pre[i]=i;  vis[i]=0; }
        for(int i=1;i<=q;++i)
        {
            int x,y;  char s[10];
            scanf("%d%d%s",&x,&y,s);
            int temp;
            if(s[0]=='n') temp = 1; else temp = 0;

            int fx=f(x) , fy=f(y);
            if(fy!=fx)
            {
                 pre[fy]=fx;
                 sum[fy]=(sum[x]-temp-sum[y]+4)%2;
            }
        }

        for(int i=0;i<maxn;++i)
        {
            who[i][0].clear();
            who[i][1].clear();
            num[i][0]=0; num[i][1]=0;
        }

        int cnt=1;
        for(int i=1;i<=(p1+p2);++i)
        {
            if(vis[i]==0)
            {
                int fx=f(i);
                for(int j=1;j<=(p1+p2);++j)
                {
                    if(f(j)==fx)
                    {
                        vis[j]=1;
                        who[cnt][sum[j]].push_back(j);
                        num[cnt][sum[j]]++;
                    }
                }
                cnt++;
            }
        }
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=1;i<cnt;++i)
        {
            for(int j=p1;j>=0;--j)
            {
                if(j>=num[i][0] && dp[i-1][j-num[i][0]])
                {
                   dp[i][j] += dp[i-1][ j-num[i][0] ];
                   path[i][j] = j-num[i][0];
                }
                if(j>=num[i][1] && dp[i-1][j-num[i][1]])
                {
                    dp[i][j] += dp[i-1][j-num[i][1]];
                    path[i][j] = j-num[i][1] ;
                }
            }
        }
        if(dp[cnt-1][p1]!=1)
        {
            printf("no\n");
        }
        else
        {
            vector<int> ans;  ans.clear();
            int much=p1;
            for(int i=cnt-1;i>=1;--i)
            {
                int pep= much - path[i][much] ;
                if(pep==num[i][0])
                {
                    for(int j=0;j<num[i][0] ; ++j )     ans.push_back(who[i][0][j]);
                }
                else
                {
                    for(int j=0;j<num[i][1]; ++j)       ans.push_back(who[i][1][j]);
                }
                much = path[i][much] ;
            }
            sort(ans.begin() , ans.end());
            for(int i=0;i<ans.size();++i)
            {
                printf("%d\n",ans[i]);
            }
            printf("end\n");
        }
    }
    return 0;
}