写+调共4.5小时。吐了。最后改了一句话过了但是忘了改了哪句话了。
做法就是缩点后跑dag,纯暴力用bitset优化,加一堆分类讨论,主要还是练一下码力,没啥算法难度。
#include
using namespace std ;
const int maxn = 1e5 + 10 ;
const int maxm = 10000 ;
bitset pre_A[maxn] ;
bitset A[maxn] ;
bitset B[maxn] ;
bitset ban ;
bitset ans ;
int cnt2 ;
void cle()
{
for(int i = 0 ; i < maxn ; i ++)
{
pre_A[i].reset() ;
A[i].reset() ;
B[i].reset() ;
}
ban.reset() ;
ans.reset() ;
}
struct Tarjan
{
int cnt , scc_num , lay ;
vector low , dfn , belong , st , s ;
vector vis ;
vector> g ;
void init(int n)
{
cnt = scc_num = lay = 0 ;
low.resize(n + 1) ;
dfn.resize(n + 1) ;
belong.resize(n + 1) ;
st.resize(n + 1) ;
s.resize(n + 1) ;
vis.resize(n + 1) ;
g.resize(n + 1) ;
fill(vis.begin() , vis.end() , false) ;
fill(low.begin() , low.end() , 0) ;
fill(dfn.begin() , dfn.end() , 0) ;
for(int i = 1 ; i <= n ; i ++) g[i].clear() ;
}
void add(int u , int v)
{
g[u].push_back(v) ;
}
void dfs(int u)
{
vis[u] = true ;
low[u] = dfn[u] = ++ lay ;
st[++ cnt] = u ;
for(auto v : g[u])
{
if(dfn[v] == 0)
{
dfs(v) ;
low[u] = min(low[u] , low[v]) ;
}
else if(vis[v]) low[u] = min(low[u] , dfn[v]) ;
}
if(dfn[u] == low[u])
{
++ scc_num ;
do
{
belong[st[cnt]] = scc_num ;
vis[st[cnt]] = false ;
cnt -- ;
} while(st[cnt + 1] != u) ;
}
}
void cal(int n)
{
for(int i = 1 ; i <= n ; i ++)
if(dfn[i] == 0) dfs(i) ;
}
void solve()
{
vector> g2(scc_num + 1) ;
map , int> mp ;
vector in(scc_num + 1 , 0) ;
for(int i = 1 ; i <= cnt2 ; i ++)
{
for(auto v : g[i])
{
if(belong[v] != belong[i])
{
int t1 = belong[i] ;
int t2 = belong[v] ;
assert(t1 != t2) ;
if(mp.count({t1 , t2})) continue ;
mp[{t1 , t2}] = 1 ;
g2[t1].push_back(t2) ;
in[t2] ++ ;
}
}
}
queue q ;
for(int i = 1 ; i <= scc_num ; i ++)
if(in[i] == 0)
{
pre_A[i] = A[i] ;
ans |= (pre_A[i] & B[i]) ;
q.push(i) ;
}
while(!q.empty())
{
int u = q.front() ;
q.pop() ;
for(auto v : g2[u])
{
ban |= (pre_A[u] & A[v]) ;
in[v] -- ;
pre_A[v] |= pre_A[u] ;
if(in[v] == 0)
{
pre_A[v] |= A[v] ;
ans |= (pre_A[v] & B[v]) ;
q.push(v) ;
}
}
}
}
} tarjan ;
int main()
{
int T ;
scanf("%d" , &T) ;
int cc = 0 ;
while(T --)
{
cle() ;
int n ;
scanf("%d" , &n) ;
map id ;
tarjan.init(100000) ;
cnt2 = 0 ;
auto add = [&](const string &s)
{
if(s == "") return 1 ;
if(id.count(s)) return id[s] ;
id[s] = ++ cnt2 ;
return cnt2 ;
} ;
getchar() ;
for(int i = 1 ; i <= n ; i ++)
{
vector v1 , v2 ;
string s = "" ;
int f = 0 ;
while(true)
{
char c = getchar() ;
if(c == '\n') break ;
if(c == ' ') continue ;
if(c == ':')
{
if(s != "")
{
int t = add(s) ;
if(f == 0) v1.push_back(t) ;
else v2.push_back(t) ;
}
s = "" ;
f = 1 ;
continue ;
}
if('A' <= c && c <= 'Z')
{
if(s != "")
{
int t = add(s) ;
if(f == 0) v1.push_back(t) ;
else v2.push_back(t) ;
}
s = "" ;
}
s += c ;
}
if(s != "")
{
int t = add(s) ;
if(f == 0) v1.push_back(t) ;
else v2.push_back(t) ;
}
assert(v1.size() == 1) ;
for(auto u : v2) tarjan.add(v1[0] , u) ;
}
int q ;
scanf("%d" , &q) ;
getchar() ;
tarjan.cal(cnt2) ;
for(int i = 0 ; i < q ; i ++)
{
vector v1 , v2 ;
string s = "" ;
int f = 0 ;
while(true)
{
char c = getchar() ;
if(c == '\n') break ;
if(c == ' ') continue ;
if(c == ':')
{
if(s != "")
{
int t = add(s) ;
if(f == 0) v1.push_back(t) ;
else v2.push_back(t) ;
}
s = "" ;
f = 1 ;
continue ;
}
if('A' <= c && c <= 'Z')
{
if(s != "")
{
int t = add(s) ;
if(f == 0) v1.push_back(t) ;
else v2.push_back(t) ;
}
s = "" ;
}
s += c ;
}
if(s != "")
{
int t = add(s) ;
if(f == 0) v1.push_back(t) ;
else v2.push_back(t) ;
}
assert(v2.size() == 1) ;
for(auto u : v1)
{
int tt = tarjan.belong[u] ;
if(A[tt][i] == 1) ban[i] = 1 ;
else A[tt][i] = 1 ;
}
for(auto u : v2) B[tarjan.belong[u]][i] = 1 ;
}
assert(cnt2 <= 1e5) ;
tarjan.solve() ;
printf("Case #%d:\n" , ++ cc) ;
for(int i = 0 ; i < q ; i ++)
{
if(ban[i] == 1) puts("Invalid") ;
else if(ans[i] == 0) puts("No") ;
else puts("Yes") ;
}
puts("") ;
}
return 0 ;
//you should actually read the stuff at the bottom before submitting or in the confusion
}
/* stuff you should look for
* long long
* array bounds
* init
* ios
* special cases (n=1?)
* do smth instead of nothing and stay organized
* WRITE STUFF DOWN
* DON'T GET STUCK ON ONE APPROACH
* DON'T GET STUCK ON ONE PROBLEM
*/
/*
1
2
A1: A2
A2: A3
1
A1: A4
*/
难点在于题意的理解。
一棵树,每个点有一个小权值,初值为0。修改和询问操作如下:
数据范围assert发现是不超过1e5。然后我也是第一次见限制输入1MB。
特别说明,一个点初始都是有一个小权值且没有大权值,如果被大权值覆盖了就没有小权值这个概念了。如果有两个点,分别具有1000的小权值和1的大权值,那么1000的小权值更大,回答1000。大权值只是在覆盖时具有更大的优先级,但是不具有数值上的优先级。
看知乎似乎是逆十字按照正确题意做的但是比赛时数据错误而没有通过。补题时数据为正确数据。
比赛时似乎是数据没有考虑根节点到节点i的权值最大值这一部分。单单考虑子树内的最小值及最大值。
#include
using namespace std ;
const int maxn = 1e6 + 10 ;
int cnt = 0 ;
string op[maxn] ;
int a[maxn] ;
string path[maxn] ;
int cur = 0 ;
int cur2 = 1 ;
map id ;
map g[maxn] ;
int siz[maxn] ;
int tt = 0 ;
int dfn[maxn] ;
int idd[maxn] ;
int cc[maxn] , dd[maxn] ;
int add(string s)
{
int now = 1 ;
//cout << "???\n" ;
for(int i = 0 ; i < s.size() ; i ++)
{
if(s[i] == '/') continue ;
int j = i ;
while(j + 1 < s.size() && j + 1 < s.size() && s[j + 1] != '/') j ++ ;
string tmp = "" ;
for(int k = i ; k <= j ; k ++) tmp += s[k] ;
if(!id.count(tmp)) id[tmp] = ++ cur ;
int t = id[tmp] ;
if(!g[now][t]) g[now][t] = ++ cur2 ;
now = g[now][t] ;
//cout << now << '\n' ;
i = j ;
}
return now ;
}
void dfs(int u)
{
tt ++ ;
dfn[u] = tt ;
siz[u] = 1 ;
for(auto x : g[u])
{
int v = x.second ;
dfs(v) ;
siz[u] += siz[v] ;
}
}
struct Info
{
bool ban ;
int small_mx ;
int big_mx ;
int small_lazy ;
int big_lazy ;
Info(bool x = false , int t1 = 0 , int t2 = 0 , int t3 = 1e9 , int t4 = -1e9 , int t5 = 0 , int t6 = 0)
{
ban = x ;
small_mx = t2 ;
big_mx = t4 ;
small_lazy = t5 ;
big_lazy = t6 ;
}
} ;
int lson(int id)
{
return id << 1 ;
}
int rson(int id)
{
return id << 1 | 1 ;
}
Info t[maxn << 2] ;
void push_down(int id)
{
if(t[lson(id)].ban == false)
{
t[lson(id)].small_mx = max(t[lson(id)].small_mx , t[id].small_lazy) ;
t[lson(id)].small_lazy = max(t[lson(id)].small_lazy , t[id].small_lazy) ;
}
t[lson(id)].big_mx = max(t[lson(id)].big_mx , t[id].big_lazy) ;
t[lson(id)].big_lazy = max(t[lson(id)].big_lazy , t[id].big_lazy) ;
if(t[rson(id)].ban == false)
{
t[rson(id)].small_mx = max(t[rson(id)].small_mx , t[id].small_lazy) ;
t[rson(id)].small_lazy = max(t[rson(id)].small_lazy , t[id].small_lazy) ;
}
t[rson(id)].big_mx = max(t[rson(id)].big_mx , t[id].big_lazy) ;
t[rson(id)].big_lazy = max(t[rson(id)].big_lazy , t[id].big_lazy) ;
t[id].small_lazy = 0 ;
t[id].big_lazy = 0 ;
}
void push_up(int id)
{
t[id].ban = (t[lson(id)].ban & t[rson(id)].ban) ;
t[id].small_mx = max(t[lson(id)].small_mx , t[rson(id)].small_mx) ;
t[id].big_mx = max(t[lson(id)].big_mx , t[rson(id)].big_mx) ;
}
void update(int id , int l , int r , int x , int y , int z)
{
if(y < l || r < x || t[id].ban == true) return ;
if(x <= l && r <= y)
{
t[id].ban = false ;
t[id].small_mx = max(t[id].small_mx , z) ;
t[id].small_lazy = max(t[id].small_lazy , z) ;
return ;
}
push_down(id) ;
int mid = (l + r) / 2 ;
update(lson(id) , l , mid , x , y , z) ;
update(rson(id) , mid + 1 , r , x , y , z) ;
push_up(id) ;
}
void update2(int id , int l , int r , int x , int y , int z)
{
if(y < l || r < x) return ;
if(x <= l && r <= y && t[id].ban == true)
{
t[id].big_mx = max(t[id].big_mx , z) ;
t[id].big_lazy = max(t[id].big_lazy , z) ;
return ;
}
if(l == r)
{
t[id].ban = true ;
t[id].small_mx = -1e9 ;
t[id].small_lazy = -1e9 ;
t[id].big_mx = z ;
t[id].big_lazy = 0 ;
return ;
}
int mid = (l + r) / 2 ;
push_down(id) ;
update2(lson(id) , l , mid , x , y , z) ;
update2(rson(id) , mid + 1 , r , x , y , z) ;
push_up(id) ;
}
int query(int id , int l , int r , int x , int y)
{
int res = 0 ;
if(y < l || r < x) return res ;
if(x <= l && r <= y)
{
res = max(t[id].small_mx , t[id].big_mx) ;
return res ;
}
int mid = (l + r) / 2 ;
push_down(id) ;
int res1 = query(lson(id) , l , mid , x , y) ;
int res2 = query(rson(id) , mid + 1 , r , x , y) ;
return max(res1 , res2) ;
}
int cal(string s)
{
int t = -1 ;
int now = 1 ;
if(dd[now] < 0) t = max(t , cc[now]) ;
else t = max(t , dd[now]) ;
//cout << "???\n" ;
for(int i = 0 ; i < s.size() ; i ++)
{
if(s[i] == '/') continue ;
int j = i ;
while(j + 1 < s.size() && j + 1 < s.size() && s[j + 1] != '/') j ++ ;
string tmp = "" ;
for(int k = i ; k <= j ; k ++) tmp += s[k] ;
if(!id.count(tmp)) id[tmp] = ++ cur ;
int t = id[tmp] ;
if(!g[now][t]) g[now][t] = ++ cur2 ;
now = g[now][t] ;
if(dd[now] < 0) t = max(t , cc[now]) ;
else t = max(t , dd[now]) ;
//cout << now << '\n' ;
i = j ;
}
return t ;
}
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
cnt = 0 ;
while(cin >> op[++ cnt])
{
if(op[cnt][0] != 'g') cin >> a[cnt] ;
cin >> path[cnt] ;
}
cnt -- ;
for(int i = 1 ; i <= cnt ; i ++) idd[i] = add(path[i]) ;
assert(cur2 <= 1e5) ;
for(int i = 1 ; i <= cur2 ; i ++) cc[i] = 0 , dd[i] = -1 ;
dfs(1) ;
int f = 0 ;
for(int i = 1 ; i <= cnt ; i ++)
{
if(op[i][0] == 'g')
{
int ttttt = cal(path[i]) ;
int ttt2 = query(1 , 1 , cur2 , dfn[idd[i]] , dfn[idd[i]]) ;
int ttt = query(1 , 1 , cur2 , dfn[idd[i]] , dfn[idd[i]] + siz[idd[i]] - 1) ;
if(f == 1) cout << '\n' ;
cout << max(ttttt , ttt2) << ' ' << max(ttttt , ttt) ;
f = 1 ;
}
else if(op[i][0] == 'd')
{
cc[idd[i]] = max(cc[idd[i]] , a[i]) ;
update(1 , 1 , cur2 , dfn[idd[i]] , dfn[idd[i]] + siz[idd[i]] - 1 , a[i]) ;
}
else
{
dd[idd[i]] = max(dd[idd[i]] , a[i]) ;
update2(1 , 1 , cur2 , dfn[idd[i]] , dfn[idd[i]] + siz[idd[i]] - 1 , a[i]) ;
}
}
return 0 ;
//you should actually read the stuff at the bottom before submitting or in the confusion
}
/* stuff you should look for
* long long
* array bounds
* init
* ios
* special cases (n=1?)
* do smth instead of nothing and stay organized
* WRITE STUFF DOWN
* DON'T GET STUCK ON ONE APPROACH
* DON'T GET STUCK ON ONE PROBLEM
*/