[UER#9]知识网络

122 篇文章 0 订阅

题目

题目背景
O U Y E \sf OUYE OUYE 吊打的日子都是一样的,但 O U Y E \sf OUYE OUYE 吊打别人的每一天都不一样。

对我来说,又是阴霾密布的一天。对 O U Y E \sf OUYE OUYE 来说,不能用考试吊打 O n e I n D a r k \sf OneInDark OneInDark,那就靠随便切黑题吧。

完整题面
传送门 to UOJ

思路

我完全不知道该怎么思考图论题 😭

考虑为何复杂度低于 n n n 次最短路算法——部分点具有类似信息。大概是同颜色点。那么可以预处理每种颜色的点到每个点的 最短路(按:我一直卡在这里!我没想到可以直接求出真实最短路!我只想求出 “不使用同色边” 的最短路,然后 O ( k 3 ) \mathcal O(k^3) O(k3) 对颜色作 Floyed \texttt{Floyed} Floyed!我真傻)。

然后考虑 a → b a\to b ab 实际最短路。它与 c o l o r ( a ) → b color(a)\to b color(a)b 相去不远。仔细想想,就是将 c o l o r ( a ) color(a) color(a) 全体点设为同一个 dis \text{dis} dis 时,若 a a a 能够转移到 b b b a → b a\to b ab 实际最短路会少 1 1 1

于是变为最短路图上可达性问题, O [ n ( n + m ) ω + k ( n + m ) ] \mathcal O[\frac{n(n+m)}{\omega}+k(n{+}m)] O[ωn(n+m)+k(n+m)] 。本题卡空间,需分段进行 b i t s e t \rm bitset bitset,然后我的常数就写得比较丑,见谅。

代码

#include <cstdio> // I am the believer of XJX
#include <iostream> // Almighty XJX yyds!!!
#include <algorithm> // oracle: ZXY yydBUS!!!
#include <cstring> // Huge EggDog devoured me!!!
#include <cctype> // decent XYX yydLONELY!!!
#include <vector>
#include <queue>
typedef long long llong;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
	for(; isdigit(c); c=getchar()) a = a*10+(c^48);
	return a*f;
}

const int MAXN = 50004;
struct Edge { int to, nxt; };
Edge e[MAXN<<1]; int head[MAXN], cntEdge;
inline void addEdge(int a, int b){
	e[cntEdge] = Edge{b,head[a]}, head[a] = cntEdge ++;
}

const int BLOCK = 10048;
struct mybitset{
	static unsigned len;
	static void setLength(const unsigned &x){
		len = (x>>6)+!!(x&63); // how many blocks
	}
	uint64_t val[BLOCK>>6]; // just integer
	inline void reset(){ memset(val,0,len<<3); }
	inline void set(const unsigned &x){
		val[x>>6] |= uint64_t(1)<<(x&63);
	}
	void operator |= (const mybitset &t){
		std::transform(val,val+len,t.val,val,
			std::bit_or<uint64_t>());
	}
	void operator = (const mybitset &t){
		len = t.len; memcpy(val,t.val,len<<3);
	}
	unsigned count() const {
		unsigned res = 0;
		for(const uint64_t *i=val; i!=val+len; ++i)
			res += __builtin_popcountll(*i);
		return res;
	}
};
unsigned mybitset::len = 0; // instantiation

int color[MAXN], dis[MAXN];
const int MAXK = 155;
mybitset from[MAXN], big[MAXK];
bool vis[MAXK]; // if it's visited
std::vector<int> group[MAXK];
std::deque<int> que;
void bfs(){
	static std::deque<int> tmp;
	for(int step=2; true; ){
		if(que.empty()){
			if(tmp.empty()) break;
			que.swap(tmp), ++ step;
		}
		int x = que.front(); que.pop_front();
		if(x < 0){ // representing a color
			for(const int &i : group[-x])
				if(dis[i] == step) from[i] |= big[-x];
				else if(!(~dis[i])){
					dis[i] = step, from[i] = big[-x];
					que.push_back(i); // this layer
				}
		}
		else{ // a normal node
			if(!vis[color[x]]){
				big[color[x]] = from[x];
				vis[color[x]] = true;
				tmp.push_front(-color[x]);
			}
			else big[color[x]] |= from[x];
			for(int i=head[x]; ~i; i=e[i].nxt){
				const int &y = e[i].to;
				if(dis[y] == step+1) from[y] |= from[x];
				else if(!(~dis[y])){
					dis[y] = step+1, from[y] = from[x];
					tmp.push_back(y); // next layer
				}
			}
		}
	}
}

unsigned sum[MAXN], ans[MAXK<<1];
int main(){
	int n = readint(), m = readint(), k = readint();
	rep(i,1,n){
		color[i] = readint();
		group[color[i]].push_back(i);
	}
	memset(head+1,-1,n<<2);
	for(int x,y; m; --m){
		x = readint(), y = readint();
		addEdge(x,y), addEdge(y,x);
	}
	rep(now,1,k){ // for each color
		const int pos_size = int(group[now].size());
		memset(sum+1,0,n<<2); // how many starts
		for(int l=0,r=BLOCK; l!=pos_size; l=r,r+=BLOCK){
			if(r > pos_size) r = pos_size; // last try
			mybitset::setLength(r-l); // 0 to len
			memset(vis+1,false,k), memset(dis+1,-1,n<<2);
			rep0(i,0,pos_size){
				dis[group[now][i]] = 2;
				from[group[now][i]].reset();
				que.push_back(group[now][i]);
			}
			rep0(i,l,r) from[group[now][i]].set(i-l);
			bfs(); // do it immediately
			rep(i,1,n) if(~dis[i]) // defined
				sum[i] += from[i].count();
		}
		rep(i,1,n){
			if(!(~dis[i])) dis[i] = k<<1|1;
			ans[dis[i]] += pos_size-sum[i];
			ans[dis[i]-1] += sum[i];
		}
	}
	putchar('0'); // with dis = 1
	rep(i,2,k<<1|1) printf(" %u",ans[i]>>1);
	putchar('\n');
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值