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;
}