2016-2017 ACM-ICPC Southwestern European Regional Programming Contest (SWERC 2016) 部分題解
阿新 • • 發佈:2021-02-19
Problem A: Within Arm's Reach
留坑。
Problem B: Bribing Eve
看作點積相乘。把看作單位向量,有一個的取值範圍,得到個符合這個式子的取值範圍,對取值範圍求交。可以得到排名的最大值和最小值。
#include <bits/stdc++.h> #define fi first #define se second using namespace std; using pii = pair<int, int>; using db = long double; const int N = 1e5 + 5; const db eps = 1e-10; inline int sgn(db x) { return (x<-eps ? -1 : x>eps); } int n; pii p[N]; int q[N]; int mul(pii a, pii b) { return a.fi*b.fi + a.se*b.se; } db div(pii a, pii b) { return db(b.se-a.se)/(a.fi-b.fi); } pii gao2(pii t) { for(int i=1; i<=n; i++) q[i] = mul(t, p[i]); int tag = q[1]; sort(q+1, q+n+1); reverse(q+1, q+n+1); int mn = 0, mx = 0; for(int i=1; i<=n; i++) if(q[i]==tag) mx = i; for(int i=1; i<=n; i++) if(q[i]==tag) { mn = i; break; } return {mn, mx}; } pii gao() { vector<db> le, ge; int exmn = 0, exmx = 0; for(int i=2; i<=n; i++) { if(p[1].fi>p[i].fi) { if(p[1].se<=p[i].se) le.push_back(div(p[1], p[i])); } else if(p[1].fi<p[i].fi) { if(p[1].se>=p[i].se) ge.push_back(div(p[1], p[i])); else exmx++, exmn++; } else if(p[i].se>=p[1].se) { ++exmx; if(p[i].se>p[1].se) ++exmn; } } sort(begin(le), end(le)); sort(begin(ge), end(ge)); int lesz = le.size(), gesz = ge.size(); int mn = min(gesz, lesz), mx = max(gesz, lesz); { int j = -1, k = -1; for(int i=0; i<gesz; i++) { while(j+1<lesz && sgn(le[j+1]-ge[i])==-1) ++j; while(k+1<lesz && sgn(le[k+1]-ge[i])<=0) ++k; mx = max(mx, i+1+lesz-j-1); mn = min(mn, i+lesz-k-1); } } { int j = -1, k = -1; for(int i=0; i<lesz; i++) { while(j+1<gesz && sgn(ge[j+1]-le[i])<=0) ++j; while(k+1<gesz && sgn(ge[k+1]-le[i])<0) ++k; mx = max(mx, lesz-i+j+1); mn = min(mn, lesz-i-1+k+1); } } return {mn+exmn+1, mx+exmx+1}; } int main() { scanf("%d", &n); for(int i=1; i<=n; i++) scanf("%d%d", &p[i].fi, &p[i].se); auto ans = gao(); auto ans2 = gao2({1, 0}); printf("%d %d\n", min(ans.fi, ans2.fi), max(ans.se, ans2.se)); return 0; } /* 5 7 7 11 10 8 5 1 1 12 12 */
Problem C: Candle Box
溫暖的簽到題。
#include<bits/stdc++.h> using namespace std; int main(){ int d,r,t; int cnt=0,cr=0; scanf("%d %d %d",&d,&r,&t); for(int i=1;i<=2000;i++){ if(i>=4)cnt+=i,cr+=i; if(i-d>=3)cnt+=i-d; if(r+t==cnt){ printf("%d\n",r-cr); break; } } return 0; }
Problem D: Dinner Bet
表示第一個人和第二個人共有的數字出現個,第一個人專屬的數字出現個,第二個人專屬的數字出現個。
#include<bits/stdc++.h> using namespace std ; double dp[55][55][55] ; int a[55] , b[55] ; double C[55][55] ; int main() { std::ios::sync_with_stdio(false) , cin.tie(0) ; int n , d , c ; cin >> n >> d >> c ; for(int i = 1 ; i <= c ; i ++) cin >> a[i] ; for(int i = 1 ; i <= c ; i ++) cin >> b[i] ; C[0][0] = 1 ; for(int i = 1 ; i <= n ; i ++) for(int j = 0 ; j <= i ; j ++) if(j > 0) C[i][j] = C[i - 1][j] + C[i - 1][j - 1] ; else C[i][j] = C[i - 1][j] ; int com = 0 ; for(int i = 1 ; i <= c ; i ++) for(int j = 1 ; j <= c ; j ++) if(a[i] == b[j]) com ++ ; for(int i = 0 ; i <= com ; i ++) for(int j = 0 ; j <= c - com ; j ++) for(int k = 0 ; k <= c - com ; k ++) dp[i][j][k] = -100 ; function<double(int , int , int)> dfs = [&](int x , int y , int z) { if(x + y == c || x + z == c) { dp[x][y][z] = 0.0 ; return 0.0 ; } if(dp[x][y][z] > -1) return dp[x][y][z] ; double res = 0 ; double t = 0 ; for(int i = 0 ; i <= com - x ; i ++) for(int j = 0 ; j <= c - com - y ; j ++) for(int k = 0 ; k <= c - com - z ; k ++) { if(i == 0 && j == 0 && k == 0) { t = 1.0 * C[com - x][i] * C[c - com - y][j] * C[c - com - z][k] * C[n - (com - x) - (c - com - y) - (c - com - z)][d - i - j - k] / C[n][d] ; } else { if(d < i + j + k) continue ; dfs(x + i , y + j , z + k) ; res += dp[x + i][y + j][z + k] * C[com - x][i] * C[c - com - y][j] * C[c - com - z][k] * C[n - (com - x) - (c - com - y) - (c - com - z)][d - i - j - k] / C[n][d] ; } } dp[x][y][z] = (1 + res) / (1 - t) ; return dp[x][y][z] ; } ; cout << fixed << setprecision(12) << dfs(0 , 0 , 0) << '\n' ; return 0 ; }
Problem E: Passwords
表示長度是的字串中含有個字母且位於字典圖上第個節點的方案數。
下面分析怎麼統計答案。
- 至少出現一個大寫字母和一個小寫字母。但是狀態不用區分大小寫。只要去掉全部大寫和全部小寫的兩種情況即可。
- 至少出現一個數字。只要即可。
- 有些數字可以被當作字母。數字當作對應的字元在字典圖上轉移即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 4e3 + 5, mod = 1000003;
int a, b, n;
char s[25];
int dp[25][25][N];
inline void Add(int &x, int y) { x += y; if(x>=mod) x -= mod; }
struct ACA
{
int nxt[N][36], fail[N], end[N], cnt;
void insert(char *s)
{
int p = 0;
for(int i=0; s[i]; i++)
{
int k = s[i] - 'a';
if(!nxt[p][k]) nxt[p][k] = ++cnt;
p = nxt[p][k];
}
end[p] = 1;
}
void build()
{
queue<int> q;
for(int i=0; i<26; i++) if(nxt[0][i]) q.push(nxt[0][i]);
while(!q.empty())
{
int k = q.front(); q.pop();
for(int i=0; i<26; i++)
{
if(nxt[k][i])
{
fail[nxt[k][i]] = nxt[fail[k]][i];
end[nxt[k][i]] |= end[fail[nxt[k][i]]];
q.push(nxt[k][i]);
}
else nxt[k][i] = nxt[fail[k]][i];
}
}
}
int type(int c)
{
if(c==26) return 'o'-'a';
if(c==27) return 'i'-'a';
if(c==29) return 'e'-'a';
if(c==31) return 's'-'a';
if(c==33) return 't'-'a';
return c;
}
void run()
{
dp[0][0][0] = 1;
for(int i=1; i<=b; i++)
for(int j=0; j<=i; j++)
for(int p=0; p<=cnt; p++) if(dp[i-1][j][p])
for(int c=0; c<36; c++)
{
int tc = type(c);
int np = nxt[p][tc];
if(end[np]) continue;
Add(dp[i][j+(c<26)][np], dp[i-1][j][p]);
}
}
}ac;
int f(int x)
{
int ans = 0;
for(int i=0; i<=ac.cnt; i++)
for(int j=2; j<x; j++)
{
if(!dp[x][j][i]) continue;
int cur = 1ll*((1<<j)-2)*dp[x][j][i]%mod;
Add(ans, cur);
}
return ans;
}
int main()
{
scanf("%d%d%d", &a, &b, &n);
for(int i=0; i<n; i++)
{
scanf("%s", s);
ac.insert(s);
}
ac.build();
ac.run();
int ans = 0;
for(int i=a; i<=b; i++) Add(ans, f(i));
printf("%d\n", ans);
return 0;
}
/*
3 5
9
swerc
icpc
fbi
cia
bio
z
hi
no
yes
*/
Problem F: Performance Review
按照排序後樹狀陣列維護答案即可。
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e5 + 10 ;
struct BIT
{
ll tree[maxn] ; //開 1 倍空間
int n ;
void init(int up)
{
n = up ;
}
int lowbit(int k)
{
return k & -k ;
}
void add(int x , ll k) // a[x] += k
{
while(x <= n) //維護的是 [1 , n] 的序列
{
tree[x] += k ;
x += lowbit(x) ;
}
}
ll sum(int x) // sum[l , r] = sum(r) - sum(l - 1)
{
ll ans = 0 ;
while(x != 0)
{
ans += tree[x] ;
x -= lowbit(x) ;
}
return ans ;
}
ll query(int l , int r)
{
return sum(r) - sum(l - 1) ;
}
} bit ;
struct node
{
int id , r , val ;
int dfn , siz ;
bool operator < (const node &s)
{
return r < s.r ;
}
} p[maxn] ;
int cnt ;
vector<int> g[maxn] ;
ll ans[maxn] ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n ;
int rt ;
cin >> n ;
bit.init(n) ;
for(int i = 1 ; i <= n ; i ++)
{
int fa ;
cin >> fa ;
cin >> p[i].r >> p[i].val ;
p[i].id = i ;
if(fa != -1) g[fa].push_back(i) ;
else rt = i ;
}
function<void(int)> dfs = [&](int u)
{
p[u].dfn = ++ cnt ;
p[u].siz = 1 ;
for(auto v : g[u])
{
dfs(v) ;
p[u].siz += p[v].siz ;
}
} ;
dfs(rt) ;
sort(p + 1 , p + n + 1) ;
for(int i = 1 ; i <= n ; i ++)
{
int j = i ;
while(j + 1 <= n && p[j + 1].r == p[j].r) j ++ ;
for(int k = i ; k <= j ; k ++) ans[p[k].id] += bit.query(p[k].dfn , p[k].dfn + p[k].siz - 1) ;
for(int k = i ; k <= j ; k ++) bit.add(p[k].dfn , p[k].val) ;
i = j ;
}
for(int i = 1 ; i <= n ; i ++) cout << ans[i] << '\n' ;
return 0 ;
}
Problem G: Cairo Corridor
留坑。
Problem H: Pascal's Hyper-Pyramids
爆搜剪枝即可。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using D = array<int, 32>;
int d, h;
set<ll> st;
D dim;
ll C[35][35];
void dfs(int dcnt)
{
if(dcnt==d-1)
{
int sum = 0;
for(int i=0; i<=dcnt; i++) sum += dim[i];
if(sum==h-1)
{
ll ans = 1;
for(int i=0; i<=dcnt; i++)
ans *= C[sum][dim[i]], sum -= dim[i];
st.insert(ans);
}
return;
}
int sum = 0, pre = 0;
for(int i=0; i<=dcnt; i++) sum += dim[i];
if(~dcnt) pre = dim[dcnt];
for(int i=pre; sum+i<h; i++)
{
dim[dcnt+1] = i;
dfs(dcnt+1);
}
}
int main()
{
for(int i=0; i<=32; i++) C[i][0] = C[i][i] = 1;
for(int i=2; i<=32; i++)
for(int j=1; j<i; j++) C[i][j] = C[i-1][j] + C[i-1][j-1];
scanf("%d%d", &d, &h);
dfs(-1);
for(auto x : st) printf("%lld\n", x);
return 0;
}
Problem I: The White Rabbit Pocket Watch
模意義下的高斯消元。然後跑。
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 500 + 10 ;
const int mod = 13 ;
struct Gauss
{
int n ;
int a[maxn][maxn] ; //需要保證這個陣列的值都小於mod
int inv[maxn] ;
int ans[maxn] ;
int qpow(int a , int b) //快速冪
{
if(b < 0) return 0 ;
int ans = 1 ;
a %= mod ;
while(b)
{
if(b & 1) ans = 1ll * ans * a % mod ;
b >>= 1 , a = 1ll * a * a % mod ;
}
return ans ;
}
void init(int tmp)
{
n = tmp ;
//如果模數小,可以考慮預處理逆元
for(int i = 1 ; i <= mod - 1 ; i ++) inv[i] = qpow(i , mod - 2) ;
}
void cal()
{
for(int i = 1 ; i <= n ; i ++)
{
if(!a[i][i])//主元不能為0
{
int id = 0 ;
int tmp ;
for(int j = i + 1 ; j <= n && !id ; j ++)
if(a[j][i]) id = j ;
if(!id) continue ; //如果一整列都為0,不需要消元
for(int j = i ; j <= n + 1 ; j ++)
tmp = a[id][j] , a[id][j] = a[i][j] , a[i][j] = tmp ;
}
for(int j = i + 1 ; j <= n ; j ++)
{
int tmp = a[j][i] ;
if(!tmp) continue ; //已經為0,不需要消元
for(int k = i ; k <= n + 1 ; k ++)
{
a[j][k] = (1ll * a[j][k] * a[i][i] % mod - 1ll * a[i][k] * tmp % mod) % mod ;
if(a[j][k] < 0) a[j][k] += mod ;
}
}
}
for(int i = n ; i >= 1 ; i --)
{
for(int j = i + 1 ; j <= n ; j ++)
{
a[i][n + 1] -= 1ll * ans[j] * a[i][j] % mod ;
a[i][n + 1] %= mod ;
if(a[i][n + 1] < 0) a[i][n + 1] += mod ;
}
ans[i] = 1ll * a[i][n + 1] * inv[a[i][i]] % mod ;
}
}
} gauss ;
int id[maxn][maxn] ;
int d[maxn][maxn] ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n , a , r , t ;
cin >> n >> a >> r >> t ;
int cnt = 0 ;
gauss.init(500) ;
for(int i = 1 ; i <= t ; i ++)
{
int d , p ;
cin >> d >> p ;
int x = 0 , y = 0 ;
for(int j = 1 ; j <= p ; j ++)
{
x = y ;
cin >> y ;
if(x == 0) continue ;
if(id[x][y] == 0) id[x][y] = id[y][x] = ++ cnt ;
gauss.a[i][id[x][y]] ++ ;
gauss.a[i][id[x][y]] %= mod ;
}
gauss.a[i][501] = d ;
}
gauss.cal() ;
memset(d , 0x3f , sizeof(d)) ;
for(int i = 1 ; i <= n ; i ++) d[i][i] = 0 ;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= n ; j ++)
if(id[i][j] != 0)
d[i][j] = gauss.ans[id[i][j]] ;
for(int k = 1 ; k <= n ; k ++)
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= n ; j ++)
d[i][j] = min(d[i][j] , d[i][k] + d[k][j]) ;
cout << d[a][r] << '\n' ;
return 0 ;
}
Problem J: Risky Lottery
留坑。
Problem K: Balls and Needles
對於一個沒有重邊的連通塊,邊數大於等於點數認為有環。
#include<bits/stdc++.h>
using namespace std ;
map<int , bool> ok[100000 + 10] ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
map<array<int , 3> , int> id ;
map<pair<int , int> , int> id2 ;
int cnt = 0 ;
int cnt2 = 0 ;
int n ;
cin >> n ;
vector<vector<int>> g(n * 2) ;
vector<vector<int>> g2(n * 2) ;
for(int i = 0 ; i < n ; i ++)
{
array<int , 3> a ;
for(int j = 0 ; j < 3 ; j ++) cin >> a[j] ;
if(!id.count(a)) id[a] = ++ cnt ;
array<int , 3> b ;
for(int j = 0 ; j < 3 ; j ++) cin >> b[j] ;
if(!id.count(b)) id[b] = ++ cnt ;
g[id[a]].push_back(id[b]) ;
g[id[b]].push_back(id[a]) ;
pair<int , int> a2 = {a[0] , a[1]} ;
pair<int , int> b2 = {b[0] , b[1]} ;
if(!id2.count(a2)) id2[a2] = ++ cnt2 ;
if(!id2.count(b2)) id2[b2] = ++ cnt2 ;
if(id2[a2] == id2[b2]) continue ;
if(!ok[id2[a2]][id2[b2]])
{
g2[id2[a2]].push_back(id2[b2]) ;
g2[id2[b2]].push_back(id2[a2]) ;
ok[id2[a2]][id2[b2]] = true ;
ok[id2[b2]][id2[a2]] = true ;
//cout << id2[a2] << ' ' << id2[b2] << '\n' ;
}
}
vector<bool> vis(cnt + 1 , false) ;
bool flag1 = false ;
int cnt11 = 0 , cnt12 = 0 ;
bool flag2 = false ;
function<void(int)> dfs = [&](int u)
{
vis[u] = true ;
cnt11 ++ ;
for(auto v : g[u])
{
cnt12 ++ ;
if(!vis[v]) dfs(v) ;
}
} ;
for(int i = 1 ; i <= cnt ; i ++)
if(!vis[i])
{
cnt11 = 0 , cnt12 = 0 , dfs(i) ;
cnt12 /= 2 ;
if(cnt12 >= cnt11) flag1 = true ;
}
vector<bool> vis2(cnt2 + 1 , false) ;
int cnt21 = 0 , cnt22 = 0 ;
function<void(int)> dfs2 = [&](int u)
{
vis2[u] = true ;
cnt21 ++ ;
for(auto v : g2[u])
{
cnt22 ++ ;
if(!vis2[v]) dfs2(v) ;
}
} ;
for(int i = 1 ; i <= cnt2 ; i ++)
if(!vis2[i])
{
cnt21 = 0 , cnt22 = 0 , dfs2(i) ;
cnt22 /= 2 ;
if(cnt22 >= cnt21) flag2 = true ;
//cout << i << ' ' << cnt21 << ' ' << cnt22 << '\n' ;
}
if(flag1) cout << "True closed chains\n" ;
else cout << "No true closed chains\n" ;
if(flag2) cout << "Floor closed chains\n" ;
else cout << "No floor closed chains\n" ;
return 0 ;
}