Problem A. Add More Zero
温暖的签到题。
#include<bits/stdc++.h>
using namespace std ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
double m ;
int cas = 0 ;
while(cin >> m)
{
cout << "Case #" << ++ cas << ": " ;
m *= log10(2) ;
cout << (int)m << '\n' ;
}
return 0 ;
}
Problem B. Balala Power!
先把每个字符的权值都当成,把每种字符的贡献算出来,然后按照贡献从大到小排序依次分配。
注意前导。
#include<bits/stdc++.h>
using namespace std ;
const int mod = 1e9 + 7 ;
const int maxn = 1e5 + 1e4 + 10 ;
int cnt[30][maxn] ;
int mx[30] ;
bool vis[30] ;
int pre[maxn] ;
bool zero[30] ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n ;
int cas = 0 ;
pre[0] = 1 ;
for(int i = 1 ; i <= 1e5 + 1e4 ; i ++) pre[i] = 1ll * pre[i - 1] * 26 % mod ;
while(cin >> n)
{
cout << "Case #" << ++ cas << ": " ;
memset(cnt , 0 , sizeof(cnt)) ;
memset(mx , -1 , sizeof(mx)) ;
memset(vis , false , sizeof(vis)) ;
memset(zero , false , sizeof(zero)) ;
while(n --)
{
string s ;
cin >> s ;
int len = s.size() ;
for(int i = 0 ; i < len ; i ++)
{
int p = len - 1 - i ;
cnt[s[i] - 'a'][p] ++ ;
mx[s[i] - 'a'] = max(mx[s[i] - 'a'] , p) ;
}
if(len > 1) zero[s[0] - 'a'] = true ;
}
for(int i = 0 ; i < 26 ; i ++)
{
for(int j = 0 ; j <= mx[i] + 50 ; j ++)
{
cnt[i][j + 1] += cnt[i][j] / 26 ;
cnt[i][j] %= 26 ;
}
for(int j = mx[i] + 50 ; j >= 0 ; j --)
if(cnt[i][j] > 0) mx[i] = max(mx[i] , j) ;
}
vector<int> v ;
int ans = 0 ;
for(int i = 0 ; i < 26 ; i ++)
{
int id = -1 ;
for(int j = 0 ; j < 26 ; j ++)
{
if(vis[j]) continue ;
if(id == -1)
{
id = j ;
continue ;
}
if(mx[j] < mx[id]) continue ;
if(mx[j] > mx[id])
{
id = j ;
continue ;
}
for(int k = mx[j] ; k >= 0 ; k --)
{
if(cnt[j][k] > cnt[id][k])
{
id = j ;
break ;
}
else if(cnt[j][k] == cnt[id][k]) continue ;
else break ;
}
}
vis[id] = true ;
v.push_back(id) ;
}
int t ;
for(int i = 25 ; i >= 0 ; i --)
{
if(!zero[v[i]])
{
t = i ;
break ;
}
}
//cout << t << '\n' ;
int tmp = v[t] ;
for(int i = t ; i < 25 ; i ++) v[i] = v[i + 1] ;
v[25] = tmp ;
for(int i = 0 ; i < 25 ; i ++)
{
int id = v[i] ;
for(int j = 0 ; j <= mx[id] ; j ++)
ans += 1ll * cnt[id][j] * (25 - i) * pre[j] % mod , ans %= mod ;
}
cout << ans << '\n' ;
}
return 0 ;
}
Problem C. Colorful Tree
考虑容斥,对于每种颜色,统计不包含该种颜色的路径数。
对于一个不包含第种颜色的极大联通子图,设的大小是,答案减去即可。
其实一个就完事,具体细节看代码。
#include<bits/stdc++.h>
using namespace std ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n ;
int cas = 0 ;
while(cin >> n)
{
cout << "Case #" << ++ cas << ": " ;
vector<int> c(n + 1) ;
for(int i = 1 ; i <= n ; i ++) cin >> c[i] ;
vector<vector<int>> g(n + 1) ;
for(int i = 1 ; i <= n - 1 ; i ++)
{
int u , v ;
cin >> u >> v ;
g[u].push_back(v) ;
g[v].push_back(u) ;
}
long long ans = 1ll * n * n * (n - 1) / 2 ;
vector<int> d(n + 1 , 0) ;
vector<int> siz(n + 1 , 0) ;
function<long long(int)> C = [&](int x)
{
return 1ll * x * (x - 1) / 2 ;
} ;
function<void(int , int)> dfs = [&](int fa , int u)
{
int tmp = d[c[u]] ;
siz[u] = 1 ;
for(auto v : g[u])
{
if(v == fa) continue ;
d[c[u]] = tmp ;
dfs(u , v) ;
ans -= C(siz[v] - (d[c[u]] - tmp)) ;
siz[u] += siz[v] ;
}
d[c[u]] = tmp + siz[u] ;
} ;
dfs(1 , 1) ;
for(int i = 1 ; i <= n ; i ++) ans -= C(n - d[i]) ;
cout << ans << '\n' ;
}
return 0 ;
}
Problem D. Division Game
留坑。
Problem E. Expectation of Division
留坑。
Problem F. Function
每个排列中长度为的环,都可以映射到中长度是的因子的环。累加贡献即可。
#include<bits/stdc++.h>
using namespace std ;
const int mod = 1e9 + 7 ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n , m ;
int cas = 0 ;
while(cin >> n >> m)
{
cout << "Case #" << ++ cas << ": " ;
vector<int> a(n) ;
vector<int> b(m) ;
for(int i = 0 ; i < n ; i ++) cin >> a[i] ;
for(int i = 0 ; i < m ; i ++) cin >> b[i] ;
vector<bool> vis1(n , false) ;
vector<bool> vis2(m , false) ;
vector<int> cnt1(n + 1 , 0) ;
vector<int> cnt2(m + 1 , 0) ;
int cnt = 0 ;
function<void(int)> dfs1 = [&](int u)
{
if(vis1[u]) return ;
vis1[u] = true ;
cnt ++ ;
dfs1(a[u]) ;
} ;
for(int i = 0 ; i < n ; i ++)
if(!vis1[i]) cnt = 0 , dfs1(i) , cnt1[cnt] ++ ;
cnt = 0 ;
function<void(int)> dfs2 = [&](int u)
{
if(vis2[u]) return ;
vis2[u] = true ;
cnt ++ ;
dfs2(b[u]) ;
} ;
for(int i = 0 ; i < m ; i ++)
if(!vis2[i]) cnt = 0 , dfs2(i) , cnt2[cnt] ++ ;
vector<int> c(n + 1 , 0) ;
for(int i = 1 ; i <= m ; i ++)
for(int j = i ; j <= n ; j += i)
c[j] += 1ll * cnt2[i] * i % mod , c[j] %= mod ;
//for(int i = 1 ; i <= n ; i ++) cout << i << ' ' << c[i] << '\n' ;
bool flag = true ;
long long ans = 1 ;
for(int i = 1 ; i <= n ; i ++)
if(cnt1[i] > 0)
{
//cout << i << ' ' << cnt1[i] << '\n' ;
if(c[i] == 0) flag = false ;
else for(int j = 1 ; j <= cnt1[i] ; j ++) ans *= c[i] , ans %= mod ;
}
if(!flag) ans = 0 ;
cout << ans << '\n' ;
}
return 0 ;
}
Problem G. Gear Up
这道题搞了好几个小时。刚开始题解半天看不懂啥意思,什么**父亲儿子角速度线速度的。
后来太烦了,手动画了几个图才搞明白是个啥,其实很简单。主要是要耐心画图。
本质是若干棵树组成的森林,每个节点其实是一个连通块,连通块内的齿轮角速度相同。边其实就代表着线速度相同。
每个连通块内的若干个节点会与父亲的连通块内节点和儿子的连通块内节点相连。具体关系需要手推了,说不清楚,题解其实说的也没法立刻明白啥意思。
主要考的是dfs序和线段树。不过外面套的壳太神了,需要耐心剥开。
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define cl(x) x.clear()
#define all(x) x.begin() , x.end()
#define rep(i , x , n) for(int i = x ; i <= n ; i ++)
#define per(i , n , x) for(int i = n ; i >= x ; i --)
#define mem0(x) memset(x , 0 , sizeof(x))
#define mem_1(x) memset(x , -1 , sizeof(x))
#define mem_inf(x) memset(x , 0x3f , sizeof(x))
#define debug(x) cerr << #x << " = " << x << '\n'
#define ddebug(x , y) cerr << #x << " = " << x << " " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 998244353 ;
const int maxn = 1e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ;
int n , m , q ;
int r[maxn] ;
vector<int> g[maxn] ;
vector<int> c[maxn] ;
bool vis[maxn] ;
int p[maxn] ;
int cnt ;
int dfn[maxn] ;
int siz[maxn] ;
int anc[maxn] ;
int L[maxn] , R[maxn] ;
bool fa[maxn] ;
bool son[maxn] ;
int l1[maxn] , r1[maxn] ;
struct Dsu
{
int pre[maxn] , siz[maxn] ;
void init(int n)
{
for(int i = 1 ; i <= n ; i ++) pre[i] = i , siz[i] = 1 ;
}
int find(int u)
{
if(pre[u] == u) return u ;
return pre[u] = find(pre[u]) ;
}
void join(int x , int y)
{
int fx = find(x) ;
int fy = find(y) ;
if(fx != fy)
{
if(siz[fx] <= siz[fy]) pre[fx] = fy , siz[fy] += siz[fx] ;
else pre[fy] = fx , siz[fx] += siz[fy] ;
}
}
} dsu ;
struct seg_tree
{
int max1[maxn << 2] , lazy[maxn << 2] ;
int ls(int x){ return x << 1 ; }
int rs(int x){ return x << 1 | 1 ; }
void push_up(int p){ max1[p] = max(max1[ls(p)] , max1[rs(p)]) ; }
void build(int id , int l , int r)
{
lazy[id] = 0 ;
if(l == r) {max1[id] = p[l] ; return ;}
int mid = (l + r) >> 1 ;
build(ls(id) , l , mid) ;
build(rs(id) , mid + 1 , r) ;
push_up(id) ;
}
void f(int id , int l , int r , int k)
{
lazy[id] += k ;
max1[id] += k ;
}
void push_down(int id , int l , int r)
{
int mid = (l + r) >> 1 ;
f(ls(id) , l , mid , lazy[id]) ;
f(rs(id) , mid + 1 , r , lazy[id]) ;
lazy[id] = 0 ;
}
void add(int id , int l , int r , int x , int y , int k)
{
if(x > y || x > r || y < l) return ;
if(x <= l && r <= y)
{
max1[id] += k ;
lazy[id] += k ;
return ;
}
push_down (id , l , r) ;
int mid = (l + r) >> 1 ;
if(x <= mid) add(ls(id) , l , mid , x , y , k) ;
if(y > mid) add(rs(id) , mid + 1 , r , x , y , k) ;
push_up(id) ;
}
int query(int id , int l , int r , int x , int y)
{
if(x > y || x > r || y < l) return inf ;
int ans = -1e9 ;
if(x <= l && r <= y) return max1[id] ;
int mid = (l + r) >> 1 ;
push_down(id , l , r) ;
if(x <= mid) ans = max(ans , query(ls(id) , l , mid , x , y)) ;
if(y > mid) ans = max(ans , query(rs(id) , mid + 1 , r , x , y)) ;
return ans ;
}
} seg ;
void dfs(int u , int val , int t)
{
dfn[u] = ++ cnt ;
p[dfn[u]] = val ;
siz[dfn[u]] = 1 ;
anc[u] = t ;
L[u] = cnt ;
for(auto x : c[u])
{
vis[x] = true ;
for(auto v : g[x])
{
if(vis[v]) continue ;
son[x] = true ;
fa[v] = true ;
l1[x] = min(l1[x] , cnt + 1) ;
dfs(dsu.find(v) , val + r[x] - r[v] , t) ;
//cout << "### " << x << ' ' << L[x] << ' ' << R[x] << '\n' ;
siz[dfn[u]] += siz[dfn[dsu.find(v)]] ;
r1[x] = cnt ;
}
}
R[u] = cnt ;
}
int main()
{
ios ;
int cas = 0 ;
while(cin >> n >> m >> q)
{
cout << "Case #" << ++ cas << ":\n" ;
dsu.init(n) ;
for(int i = 1 ; i <= n ; i ++) cin >> r[i] , r[i] = __builtin_ctz(r[i]) ;
for(int i = 1 ; i <= n ; i ++) g[i].clear() ;
for(int i = 1 ; i <= n ; i ++) c[i].clear() ;
for(int i = 0 ; i < m ; i ++)
{
int a , x , y ;
cin >> a >> x >> y ;
if(a == 1) g[x].push_back(y) , g[y].push_back(x) ;
else dsu.join(x , y) ;
}
for(int i = 1 ; i <= n ; i ++) c[dsu.find(i)].push_back(i) ;
memset(vis , false , sizeof(vis)) ;
memset(p , 0 , sizeof(p)) ;
cnt = 0 ;
memset(dfn , 0 , sizeof(dfn)) ;
memset(siz , 0 , sizeof(siz)) ;
memset(anc , 0 , sizeof(anc)) ;
memset(L , 0x3f , sizeof(L)) ;
memset(R , 0 , sizeof(R)) ;
memset(l1 , 0x3f , sizeof(l1)) ;
memset(r1 , 0 , sizeof(r1)) ;
memset(fa , false , sizeof(fa)) ;
memset(son , false , sizeof(son)) ;
for(int i = 1 ; i <= n ; i ++)
if(!vis[i] && i == dsu.find(i)) dfs(i , 0 , i) ;
seg.build(1 , 1 , cnt) ;
//for(int i = )
// cout << "!!! " << cnt << '\n' ;
// for(int i = 1 ; i <= cnt ; i ++)
// cout << "??? " << seg.query(1 , 1 , cnt , i , i) << '\n' ;
while(q --)
{
int a , x , y ;
cin >> a >> x >> y ;
y = __builtin_ctz(y) ;
if(a == 1)
{
int tmp = y - r[x] ;
int rt = dsu.find(x) ;
if(fa[x]) seg.add(1 , 1 , cnt , L[rt] , R[rt] , -tmp) ;
if(son[x]) seg.add(1 , 1 , cnt , l1[x] , r1[x] , tmp) ;
r[x] = y ;
// cout << "!!! " << cnt << '\n' ;
// for(int i = 1 ; i <= cnt ; i ++)
// cout << "??? " << seg.query(1 , 1 , cnt , i , i) << '\n' ;
}
else
{
int now = seg.query(1 , 1 , cnt , dfn[dsu.find(x)] , dfn[dsu.find(x)]) ;
int f = y - now ;
int rt = anc[dsu.find(x)] ;
int ans = f + seg.query(1 , 1 , cnt , L[rt] , R[rt]) ;
cout << fixed << setprecision(3) << ans * log(2) << '\n' ;
//cout << ans << '\n' ;
}
}
}
return 0 ;
}
Problem H. Hints of sd0061
函数线性求区间第大。
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 1e7 + 10 ;
int n , m ;
unsigned a[maxn] ;
unsigned ans[105] ;
struct node
{
int id , x ;
bool operator < (const node &s) const
{
return x < s.x ;
}
} b[105] ;
unsigned A , B , C ;
unsigned x , y , z ;
unsigned rng61()
{
unsigned t ;
x = x ^ (x << 16) ;
x = x ^ (x >> 5) ;
x = x ^ (x << 1) ;
t = x ;
x = y ;
y = z ;
z = ( t ^ x ) ^ y ;
return z ;
}
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n , m ;
int cas = 0 ;
while(cin >> n >> m >> A >> B >> C)
{
cout << "Case #" << ++ cas << ": " ;
x = A , y = B , z = C ;
for(int i = 1 ; i <= n ; i ++) a[i] = rng61() ;
for(int i = 1 ; i <= m ; i ++) cin >> b[i].x , b[i].id = i , b[i].x ++ ;
sort(b + 1 , b + m + 1) ;
for(int i = m ; i >= 1 ; i --)
{
if(i + 1 <= m && b[i].x == b[i + 1].x) ans[b[i].id] = ans[b[i + 1].id] ;
else
{
if(i == m) nth_element(a + 1 , a + b[i].x , a + n + 1) ;
else nth_element(a + 1 , a + b[i].x , a + b[i + 1].x + 1) ;
ans[b[i].id] = a[b[i].x] ;
}
}
for(int i = 1 ; i <= m ; i ++) cout << ans[i] << " \n"[i == m] ;
}
return 0 ;
}
Problem I. I Curse Myself
有点神的题。
因为是个仙人掌,所以可以去掉的非树边都在环上,相当于是每个环上去掉一条边。
因此题目转化为若干个序列,每个序列选择一个数,对这些数求和,询问第大的和。
本质是多个序列的合并问题,如果直接用大小为的维护合并过程的话,复杂度是,其中是若干个序列元素个数和。
设第个序列的元素个数是,考虑用大小为的堆维护,这样复杂度是。
复杂度证明可以通过求导推出来。具体维护过程看代码的函数。
调了半天,还好一发过了。
#include<bits/stdc++.h>
using namespace std ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n , m ;
int cas = 0 ;
while(cin >> n >> m)
{
cout << "Case #" << ++ cas << ": " ;
vector<vector<pair<int , unsigned>>> g(n + 1) ;
unsigned ans = 0 ;
unsigned sum = 0 ;
for(int i = 1 ; i <= m ; i ++)
{
int u , v , w ;
cin >> u >> v >> w ;
g[u].push_back({v , w}) ;
g[v].push_back({u , w}) ;
sum += w ;
}
unsigned k ;
cin >> k ;
vector<pair<int , unsigned>> pre(n + 1) ;
vector<bool> vis(n + 1 , false) ;
vector<unsigned> res ;
vector<unsigned> val ;
vector<unsigned> tmp ;
vector<unsigned> c(m + 1) ;
vector<unsigned> dfn(n + 1 , 0) ;
int cur = 0 ;
res.push_back(0) ;
function<void()> merge = [&]()
{
priority_queue<pair<unsigned , int>> q ;
int m = val.size() ;
sort(val.begin() , val.end()) ;
reverse(val.begin() , val.end()) ;
for(int i = 0 ; i < m ; i ++)
{
c[i] = 0 ;
q.push({val[i] + res[0] , i}) ;
}
tmp.clear() ;
while(!q.empty() && tmp.size() < k)
{
auto now = q.top() ;
q.pop() ;
tmp.push_back(now.first) ;
c[now.second] ++ ;
if(c[now.second] < res.size())
q.push({val[now.second] + res[c[now.second]] , now.second}) ;
}
swap(tmp , res) ;
} ;
function<void(int , int)> path = [&](int u , int anc)
{
if(u == anc) return ;
path(pre[u].first , anc) ;
val.push_back(pre[u].second) ;
} ;
function<void(int , int)> dfs = [&](int fa , int u)
{
vis[u] = true ;
dfn[u] = ++ cur ;
for(auto x : g[u])
{
int v = x.first ;
unsigned w = x.second ;
if(v == fa) continue ;
if(vis[v] && dfn[v] > dfn[u]) continue ;
if(vis[v] && dfn[v] < dfn[u])
{
val.clear() ;
val.push_back(w) ;
path(u , v) ;
merge() ;
continue ;
}
pre[v] = {u , w} ;
dfs(u , v) ;
}
} ;
dfs(1 , 1) ;
for(unsigned i = 0 ; i < res.size() ; i ++) ans += (i + 1) * (sum - res[i]) ;
cout << ans << '\n' ;
}
return 0 ;
}
Problem J. Journey with Knapsack
留坑。
Problem K. KazaQ’s Socks
手动模拟发现循环节即可。
#include<bits/stdc++.h>
using namespace std ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n ;
long long k ;
int cas = 0 ;
while(cin >> n >> k)
{
cout << "Case #" << ++ cas << ": " ;
if(n == 2)
{
if(k % 2 == 1) cout << "1\n" ;
else cout << "2\n" ;
}
else
{
if(k <= n) cout << k << '\n' ;
else
{
k -= n ;
k %= 2 * (n - 1) ;
if(k == 0) cout << n << '\n' ;
else if(k <= n - 1) cout << k << '\n' ;
else cout << k - (n - 1) << '\n' ;
}
}
}
return 0 ;
}
Problem L. Limited Permutation
分治裸题。
对于以为最小值的这段区间,。是这段区间的贡献。
注意判断无解即可。
#include<bits/stdc++.h>
using namespace std ;
const int mod = 1e9 + 7 ;
const int maxn = 1e6 + 10 ;
typedef long long ll ;
ll inv1[maxn] ; //乘法逆元
void init1(int up)
{
inv1[1] = 1 ;
for(int i = 2 ; i <= up ; i ++)
inv1[i] = (ll)(mod - mod / i) * inv1[int(mod % (ll)i)] % mod ;
}
ll fac[maxn] ;
ll inv[maxn] ; //阶乘逆元
void init(int up)
{
fac[0] = fac[1] = inv[0] = inv[1] = 1 ;
for(int i = 2 ; i <= up ; i ++)
{
fac[i] = fac[i - 1] * i % mod ;
inv[i] = -inv[mod % i] * (mod / i) % mod ;
while(inv[i] < 0) inv[i] += mod ;
}
for(int i = 2 ; i <= up ; i ++)
inv[i] = inv[i] * inv[i - 1] % mod ;
}
ll C(int n , int m)
{
return fac[n] * inv[m] % mod * inv[n - m] % mod ;
}
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n ;
int cas = 0 ;
init(1000000) ;
while(cin >> n)
{
cout << "Case #" << ++ cas << ": " ;
vector<int> l(n + 1) ;
vector<int> r(n + 1) ;
for(int i = 1 ; i <= n ; i ++) cin >> l[i] ;
for(int i = 1 ; i <= n ; i ++) cin >> r[i] ;
vector<vector<pair<int , int>>> pos(n + 1) ;
vector<int> cur(n + 1 , 0) ;
for(int i = 1 ; i <= n ; i ++) pos[l[i]].push_back({r[i] , i}) ;
for(int i = 1 ; i <= n ; i ++)
sort(pos[i].begin() , pos[i].end()) , reverse(pos[i].begin() , pos[i].end()) ;
function<long long(int , int)> solve = [&](int l , int r)
{
if(l > r) return 1ll ;
if(cur[l] == pos[l].size()) return 0ll ;
int nxt = pos[l][cur[l]].first ;
if(nxt != r) return 0ll ;
int id = pos[l][cur[l]].second ;
cur[l] ++ ;
return C(r - l , id - l) * solve(l , id - 1) % mod * solve(id + 1 , r) % mod ;
} ;
cout << solve(1 , n) << '\n' ;
}
return 0 ;
}