• 浙大集训day6:D.Keys


    浙大集训day6:D.Keys

    2184 / 5000

    建筑师蒂莫西设计了一款新的逃脱游戏。

    在这个游戏中,有 n 个房间编号从 0 到 n-1。

    最初,每个房间只包含一把钥匙。

    每个键都有一个类型,它是介于 0 和 n − 1,包括在内。

    房间 i(0 ≤ i ≤ n − 1) 中的钥匙类型是 r[i]。

    请注意,多个房间可能 包含相同类型的键,即值 r[i] 不一定不同。

    游戏中还有 m 个双向连接器,编号从 0 到 m − 1。

    j(0 ≤ j ≤ m − 1) 连接一对不同的房间 u[j] 和 v[j]。

    可以连接一对房间 通过多个连接器。

    游戏由单人玩,他收集钥匙并通过穿越在房间之间移动 连接器。

    我们说玩家在使用这个连接器移动时穿过了连接器 j 房间 u[j] 到房间 v[j],反之亦然。

    如果玩家已经收集了一个 之前 c[j] 类型的键。在游戏过程中的任何时候,玩家都在某个房间 x 中并且可以执行两个 动作类型: 1.在房间x中收集钥匙,其类型为r[x](除非他们已经收集了它)。

    2.遍历连接器 j,其中 u[j] = x 或 v[j] = x,如果玩家收集了类型为 c[j] 的密钥 预先。请注意,玩家永远不会丢弃他们收集的钥匙。

    玩家在一些没有钥匙的房间里开始游戏。

    从房间 s 可以到达房间 t, 如果在房间 s 开始游戏的玩家可以执行上述的一些动作序列,并且 到达房间 t。

    对于每个房间 i(0 ≤ i ≤ n − 1),将房间 i 可达的房间数表示为 p[i]。

    提摩太 想知道在 0 ≤ i ≤ n − 1 范围内达到 p[i] 最小值的索引集 i。

    输入 第一行包含两个整数 n,m(2 ≤ n, m ≤ 300000)。

    第二行包含 n 个整数 r[0]r[1] 。

    . . r[n − 1](0 ≤ r[i] ≤ n − 1)。

    在接下来的 m 行中,每行包含三个整数 u[j], v[j], c[j](0 ≤ u[j], v[j] ≤ n − 1, u[j] 6= v[ j])。

    输出 在一行中输出 n 个整数 a[i]。对于每一个 0 ≤ i ≤ n − 1,如果对于每一个这样的 j,a[i] 的值应该是 1 0 ≤ j ≤ n − 1,p[i] ≤ p[j]。否则,a[i] 的值应为 0。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=3e5+100;
    int r[maxn];
    int u[maxn],v[maxn],w[maxn];
    
    //每次枚举强联通分量
    //如果一条边两边的强联通分量的set包含这把钥匙
    //这条边就连
    
    int n,m;
    
    int dfn[maxn],low[maxn],pos[maxn],cnt,scc;
    stack<int> st;
    vector<pair<int,int> > g[maxn];
    set<int> ss[maxn];//每个分量的钥匙集合 
    set<int> sp[maxn];//缩点后的钥匙集合 
    int sz[maxn];//每个分量的点数 
    int ssp[maxn];//缩点后的点数 
    vector<int> gg[maxn];//每个联通分量内的节点
    vector<int> ggg[maxn];//缩点后的节点信息
    int ed[maxn]; 
    void tj (int x) {
    	low[x]=dfn[x]=++cnt;
    	st.push(x);
    	for (pair<int,int> it:g[x]) {
    		int y=it.first;
    		//if (!ss[x].count(w)) continue;
    		if (!low[y]) {
    			tj(y);
    			low[x]=min(low[x],low[y]);
    		} 
    		else if (!pos[y]) {
    			low[x]=min(low[x],dfn[y]);
    		}
    	}
    	if (low[x]==dfn[x]) {
    		scc++;
    		while (1) {
    			int u=st.top();
    			st.pop();
    			low[u]=low[x];
    			pos[u]=scc;
    			ssp[scc]+=sz[u];
    			if (u==x) break;
    		}
    	}
    }
    
    int main () {
    	scanf("%d%d",&n,&m);
    	int mmm=n;
    	for (int i=1;i<=n;i++) scanf("%d",r+i),ss[i].insert(r[i]),sz[i]=1,ed[i]=i,gg[i].push_back(i);
    	for (int i=1;i<=m;i++) {
    		scanf("%d%d%d",u+i,v+i,w+i);
    		u[i]++;
    		v[i]++;
    		if (r[u[i]]==w[i])g[u[i]].push_back(make_pair(v[i],w[i]));
    		if (r[v[i]]==w[i])g[v[i]].push_back(make_pair(u[i],w[i]));
    	}
    	int mm=n;
    	int tt=0;
    	int xx=5;
    	while (xx--) {
    		for (int i=1;i<=n;i++) {
    			dfn[i]=low[i]=pos[i]=0; 
    		}
    		cnt=scc=0;
    		while (st.size()) st.pop();
    		for (int i=1;i<=n;i++) {
    			if (!low[i]) tj(i);
    		}
    		tt++;
    		if (scc==mm) break;//如果和上一轮点数一样
    
    		for (int i=1;i<=n;i++) {
    			for (auto it:ss[i]) {
    				sp[pos[i]].insert(it);
    			}
    			for (int j:gg[i]) {
    				ggg[pos[i]].push_back(j);
    				ed[j]=pos[i];//更新每个点所属的vector 
    			}
    			gg[i].clear();
    			ssp[pos[i]]+=sz[i];
    			sz[i]=0;
    			ss[i].clear();
    		}
    		for (int i=1;i<=scc;i++) {
    			ss[i]=sp[i];
    			sz[i]=ssp[i];
    			sp[i].clear();
    			ssp[i]=0;
    			gg[i]=ggg[i];
    			ggg[i].clear();
    		}
    		
    		for (int i=1;i<=n;i++) g[i].clear();//清空上一轮的图 
    		for (int i=1;i<=m;i++) {
    			if (ed[u[i]]==ed[v[i]]) continue;
    			//printf("%d %d %d %d %d\n",pos[u[i]],pos[v[i]],w[i],ss[pos[u[i]]].count(w[i]),ss[pos[v[i]]].count(w[i]));
    			if (ss[ed[u[i]]].count(w[i]))g[ed[u[i]]].push_back(make_pair(ed[v[i]],w[i]));
    			if (ss[ed[v[i]]].count(w[i]))g[ed[v[i]]].push_back(make_pair(ed[u[i]],w[i]));
    		} 
    		n=scc;//更新n为新一轮的点数 
    		mm=n;//更新mm为上一轮的点数 
     	}
     	int Min=1e9;
    // 	for (int i=1;i<=mmm;i++) printf("%d ",ed[i]);
    // 	puts("");
    // 	for (int i=1;i<=n;i++) {
    // 		printf("%d: ",i);
    //		for (pair<int,int> it:g[i]) printf("%d ",it.first);
    // 		puts("");
    //	 }
     	for (int i=1;i<=n;i++) if (g[i].size()==0) Min=min(Min,sz[i]);
     	for (int i=1;i<=mmm;i++) if (g[ed[i]].size()==0&&sz[ed[i]]==Min) printf("1 ");else printf("0 ");
    } 
    
  • 相关阅读:
    【无中生有】---4----数据库设计-3
    【无中生有】---2---数据库设计-1
    redis3.0的demo实验
    redis3.0的demo实验
    那些在学习iOS开发前就应该知道的事(part 1)
    模板文件中的插件嵌入点列表
    图像处理框架 Core Image 介绍
    sharepoint services
    DiscuzX2.5数据库字典 值得学习
    Sublime Text 使用技巧
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15548856.html
Copyright © 2020-2023  润新知