CF701E
思路:这是第一次对于贡献这两个字有了一定的理解。
易得:每条边经过的次数=min(这条边左边学校个数,这条边右边需要的学校个数),为了能够达到最大,就尽量能走的边都走一遍,也就是把所有边对于答案的贡献加起来;此处看来,贡献仿佛就是可以对答案产生影响但又不一定必须要选择
int n,k,mark[maxn],cnt,head[maxn],vis[maxn],val[maxn];
ll ans=0;
struct Edge
{
int v,next;
}edge[maxn<<1];
void add_edge(int u,int v)
{
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void Initial()
{
memset(val,0,sizeof(val));
memset(head,-1,sizeof(head));
memset(mark,0,sizeof(mark));
memset(edge,0,sizeof(edge));
cnt=0;ans=0;
}
void dfs(int now)
{
if(vis[now])
return ;
//printf("now=%d,ans=%d\n",now,ans);
vis[now]=1;
if(mark[now])
val[now]=1;
for(int i=head[now];~i;i=edge[i].next)
{
int v=edge[i].v;
if(vis[v])
continue ;
dfs(v);
val[now]+=val[v];
//printf("vnow=%d,vv=%d\n",val[now],val[v]);
}
ans+=(ll)min(val[now],2*k-val[now]);
vis[now]=0;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
Initial();
for(int i=1;i<=k*2;i++)
{
int tmp;
scanf("%d",&tmp);
mark[tmp]=1;
}
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
dfs(1);
printf("%lld\n",ans);
}
return 0;
}
poj1988(节点带权值的并查集)
思路:我们要查询经过一系列操作后某个方块下的个数,就直接维护每个方块下的方块个数,但是要顺带维护树的节点的个数,
如图标出的是节点权值
首先是Find函数,直接更新即可,比如样例中2那一坨放到6上面后,2的权值还未更新,仍是1。使用find函数吧途径节点的权值全部加起来。
然后是Union函数,一开始2和4位于同一颗树上,6和1位于同一个树上,大小均为2,将f[4]改为6时,val[4]+=size[6],也就是加上父节点所在树的大小(=2);同时父节点的大小也要更新,因为多了一棵子树。
//直接主函数
int p,siz[maxn],f[maxn],val[maxn];
void Initial()
{
for(int i=1;i<=p;i++)
{
f[i]=i;
val[i]=0;
siz[i]=1;
}
}
int myfind(int x)
{
// printf("mfind: x=%d\n",x);
if(x==f[x])
return f[x];
int tmp=f[x];
f[x]=myfind(f[x]);
val[x]+=val[tmp];
return x=f[x];
}
void Union(int x,int y)
{
int fx=myfind(x);
//printf("heh\n");
int fy=myfind(y);
//printf("fx=%d,fy=%d\n",fx,fy);
if(fx!=fy)
{
// printf("fx=%d.fy=%d\n",fx,fy);
f[fx]=fy;
val[fx]+=siz[fy];
siz[fy]+=siz[fx];
}
}
int main()
{
scanf("%d",&p);
Initial();
while(p--)
{
char ch;
cin>>ch;
if(ch=='M')
{
int x,y;
scanf("%d%d",&x,&y);
Union(x,y);
}
else if(ch=='C')
{
int x;
scanf("%d",&x);
// printf("val=%d\n",val[x]);
int fx=myfind(x);
printf("%d\n",val[x]);
}
}
return 0;
}
hdu1811(拓扑排序+并查集)
思路:先处理相等的关系,将所有相等的点看成一点,不影响排序的结果,然后再利用拓扑排序看是否能排出一个序列出来,这一点我之前学习的时候没有注意到
1.若进入队列的点数小于总的点的数量,则必定存在环;等于则是一个无环图。
2.若队列中的点的数量大于了1,那么此时处于队列中的点之间的关系是不确定的。
代码:
int n,m,f[maxn],in[maxn],flag;
vector<int>v[maxn];
string ans[4];
struct STR
{
int a,b;
char ch;
}str[maxn*2];
int Find(int x)
{
if(f[x]==x)
return f[x];
return f[x]=Find(f[x]);
}
void Initial()
{
flag=1;
for(int i=1;i<=n;i++)
{
v[i].clear();
f[i]=i;
in[i]=0;
}
}
void tp()
{
queue<int>q;
while(!q.empty()) q.pop();
int cnt=0,num=0;
//printf("fi=%d\n",Find(1));
for(int i=1;i<=n;i++)
{
if(f[i]==i)
{
num++;
if(in[i]==0)
{
//printf("fi=%d\n",fi);
q.push(i);
}
}
}
while(!q.empty())
{
if(q.size()>=2)
{
flag=2;
}
int u=q.front();q.pop();cnt++;//printf("u=%d,cn=%d\n",u,v[u].size());
for(int i=0;i<v[u].size();i++)
{
int to=v[u][i];
//printf("t=%d,to=%d\n",t,to);
in[to]--;
if(in[to]==0)
{
q.push(to);
}
}
}
// printf("cbnt=%d\n",cnt);
if(cnt<num)
flag=3;
}
int main()
{
ans[1]=ans[1]+"OK";
ans[2]=ans[2]+"UNCERTAIN";
ans[3]=ans[3]+"CONFLICT";
while(scanf("%d%d",&n,&m)!=EOF)
{
Initial();
flag=1;
for(int i=1;i<=m;i++)
{
cin>>str[i].a>>str[i].ch>>str[i].b;
str[i].a++;str[i].b++;
//printf("%d b=%d\n",a,b);
int fa=Find(str[i].a),fb=Find(str[i].b);
//printf("a=%d,b=%d,fa=%d,fb=%d\n",a,b,fa,fb);
if(str[i].ch=='=')
f[fa]=fb;
}
for(int i=1;i<=m;i++)
{
if(str[i].ch=='=')
continue;
if(str[i].ch=='<')
{
int fa=Find(str[i].a),fb=Find(str[i].b);
in[fa]++;
v[fb].push_back(fa);
}
else if(str[i].ch=='>')
{
int fa=Find(str[i].a),fb=Find(str[i].b);
in[fb]++;
v[fa].push_back(fb);
}
}
tp();
cout<<ans[flag]<<endl;
}
return 0;
}