题目
题目背景
被
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 a→b 实际最短路。它与 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 a→b 实际最短路会少 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;
}