2017ICPC瀋陽部分簡要題解
阿新 • • 發佈:2020-12-22
技術標籤:# ICPC
B - Bridge
橋等價於不在環內的邊。那麼可以統計維護環內邊的個數,容斥一下得出橋的個數。
線段樹維護區間內豎邊的個數,最左豎邊的位置,最右豎邊的位置。
set維護連續的上下都有橫邊的區間的左端點和右端點。
寫了200行。
沒搞懂為什麼其他題解都是multiset。
#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 = 2e5 + 10 ; const int inf = 0x3f3f3f3f ; const double eps = 1e-6 ; set<pii> s ; int n , m ; int tot , num ; //點修改,區間min,max,sum int sum[maxn << 2] ; int mn[maxn << 2] ; int mx[maxn << 2] ; int cnt[maxn] ; int ls(int x) { return x << 1 ; } int rs(int x) { return x << 1 | 1 ; } void push_up(int id) { sum[id] = sum[ls(id)] + sum[rs(id)] ; mn[id] = min(mn[ls(id)] , mn[rs(id)]) ; mx[id] = max(mx[ls(id)] , mx[rs(id)]) ; } void build(int id , int l , int r) { if(l == r){ sum[id] = 1 , mn[id] = mx[id] = l ; return ;} int mid = (l + r) >> 1 ; build(ls(id) , l , mid) ; build(rs(id) , mid + 1 , r) ; push_up(id) ; } void add(int id , int l , int r , int x) { int mid = (l + r) / 2 ; if(l == r && l == x){ sum[id] = 1 , mn[id] = mx[id] = l ; return ;} if(x <= mid) add(ls(id) , l , mid , x) ; else add(rs(id) , mid + 1 , r , x) ; push_up(id) ; } void del(int id , int l , int r , int x) { int mid = (l + r) / 2 ; if(l == r && l == x){ sum[id] = 0 , mn[id] = inf , mx[id] = 0 ; return ;} if(x <= mid) del(ls(id) , l , mid , x) ; else del(rs(id) , mid + 1 , r , x) ; push_up(id) ; } int query_sum(int id , int l , int r , int x , int y) { int ans = 0 ; int mid = (l + r) / 2 ; if(y < x) return 0 ; //不合法的詢問 if(x <= l && r <= y) return sum[id] ; if(x <= mid) ans += query_sum(ls(id) , l , mid , x , y) ; if(y > mid) ans += query_sum(rs(id) , mid + 1 , r , x , y) ; return ans ; } int query_max(int id , int l , int r , int x , int y) { int ans = 0 ; int mid = (l + r) / 2 ; if(x <= l && r <= y) return mx[id] ; if(x <= mid) ans = max(ans , query_max(ls(id) , l , mid , x , y)) ; if(y > mid) ans = max(ans , query_max(rs(id) , mid + 1 , r , x , y)) ; return ans ; } int query_min(int id , int l , int r , int x , int y) { int ans = inf ; int mid = (l + r) / 2 ; if(x <= l && r <= y) return mn[id] ; if(x <= mid) ans = min(ans , query_min(ls(id) , l , mid , x , y)) ; if(y > mid) ans = min(ans , query_min(rs(id) , mid + 1 , r , x , y)) ; return ans ; } void init() { tot = 3 * n - 2 ; num = 3 * n - 2 ; cl(s) ; s.insert({1 , n}) ; build(1 , 1 , n) ; rep(i , 1 , n - 1) cnt[i] = 2 ; } int cal(int l , int r) { int sum = query_sum(1 , 1 , n , l , r) ; int mx = query_max(1 , 1 , n , l , r) ; int mn = query_min(1 , 1 , n , l , r) ; if(sum <= 1) return 0 ; return sum + (mx - mn) * 2 ; } void add_row(int x0 , int y0) { cnt[y0] ++ ; tot ++ ; if(cnt[y0] == 2) { auto it = s.lower_bound({y0 + 1 , 0}) ; int l2 = (*it).fi ; int r2 = (*it).se ; it -- ; int l1 = (*it).fi ; int r1 = (*it).se ; num -= cal(l1 , r1) ; num -= cal(l2 , r2) ; s.erase({l1 , r1}) ; s.erase({l2 , r2}) ; s.insert({l1 , r2}) ; num += cal(l1 , r2) ; } } void add_col(int y0) { tot ++ ; auto it = s.lower_bound({y0 + 1 , 0}) ; it -- ; int l = (*it).fi ; int r = (*it).se ; num -= cal(l , r) ; add(1 , 1 , n , y0) ; num += cal(l , r) ; } void del_row(int x0 , int y0) { cnt[y0] -- ; tot -- ; if(cnt[y0] == 1) { auto it = s.lower_bound({y0 + 1 , 0}) ; it -- ; int l1 = (*it).fi ; int r2 = (*it).se ; int r1 = y0 ; int l2 = y0 + 1 ; num -= cal(l1 , r2) ; num += cal(l1 , r1) ; num += cal(l2 , r2) ; s.erase(it) ; s.insert({l1 , r1}) ; s.insert({l2 , r2}) ; } } void del_col(int y0) { tot -- ; auto it = s.lower_bound({y0 + 1 , 0}) ; it -- ; int l = (*it).fi ; int r = (*it).se ; num -= cal(l , r) ; del(1 , 1 , n , y0) ; num += cal(l , r) ; } int main() { ios ; int T ; cin >> T ; while(T --) { cin >> n >> m ; init() ; while(m --) { int op , x0 , y0 , x1 , y1 ; cin >> op >> x0 >> y0 >> x1 >> y1 ; if(op == 1) { if(x0 == x1) add_row(x0 , min(y0 , y1)) ; else add_col(y0) ; } else { if(x0 == x1) del_row(x0 , min(y0 , y1)) ; else del_col(y0) ; } cout << tot - num << '\n' ; } } return 0 ; }
C - Empty Convex Polygons
首先列舉左下角的點,表示以為最後兩條邊的滿足題意的最大凸多邊形的面積。
轉移方程:
複雜度是
我又寫了200多行。
#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 = 100 + 10 ; const int inf = 0x3f3f3f3f ; const double eps = 1e-6 ; int n ; db dp[maxn][maxn] ; int sgn(db x) { if(x < -eps) return -1 ; else if (x > eps) return 1 ; else return 0 ; } void print(int num , db x) { cout << fixed << setprecision(num) << x << '\n' ; } struct point { db x , y ; point(){} point(db _x , db _y) { x = _x , y = _y ; } point operator + (const point& s) const { return point(x + s.x , y + s.y) ; } point operator - (const point& s) const { return point(x - s.x , y - s.y) ; } point operator * (const db& k) const { return point(x * k , y * k) ; } point operator / (const db& k) const { return point(x / k , y / k) ; } db get_angle()//極角 { return atan2(y , x) ; } bool operator == (point b) const { return sgn(x - b.x) == 0 && sgn(y - b.y) == 0 ; } bool operator < (point b)const { return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x ; } db operator ^(const point &b) const //向量叉積 { return x * b.y - y * b.x ; } db operator *(const point &b) const //向量點積 { return x * b.x + y * b.y ; } db sqr(db x) { return x * x ; } db len() //與原點的距離 { return sqrt(sqr(x) + sqr(y)) ; } db dis(point p) //返回兩點間的距離 { return sqrt(sqr(x - p.x) + sqr(y - p.y)) ; } db rad(point a , point b) //向量p->a和p->b的夾角 { point p = *this ; return fabs(atan2(fabs((a - p) ^ (b - p)) , (a - p) * (b - p))) ; } point trunc(db r) //化為長度為r的向量 { db l = len() ; if(!sgn(l)) return *this ; r /= l ; return point(x * r , y * r) ; } point rot_left() //逆時針旋轉90度 { return point(-y , x) ; } point rot_right() //順時針旋轉90度 { return point(y , -x) ; } point rotate(point p , db angle) //繞a點逆時針旋轉angle { point v = (*this) - p ; db c = cos(angle) , s = sin(angle) ; return point(p.x + v.x * c - v.y * s , p.y + v.x * s + v.y * c) ; } } p[maxn] ; struct line { point s , e ; line(){} line(point _s , point _e) { s = _s , e = _e ; } db len() { return s.dis(e) ; } void adjust() { if(e < s) swap(s , e) ; } db dis_point_to_line(point p) //點a到直線的距離 { return fabs((p - s) ^ (e - s)) / len() ; } db dis_point_to_seg(point p) //點a到線段的距離 { if(sgn((p - s) * (e - s)) < 0 || sgn((p - e) * (s - e)) < 0) return min(p.dis(s) , p.dis(e)) ; else return dis_point_to_line(p) ; } pair<point,int> operator &(const line &b) const //直線相交求交點 { point res = s ; if(sgn((s - e) ^ (b.s - b.e)) == 0) { if(sgn((b.s - s) ^ (b.e - s)) == 0) return make_pair(res , 0) ; //兩直線重合 else return make_pair(res , 1) ; //兩直線平行 } db t = ((s - b.s) ^ (b.s - b.e)) / ((s - e) ^ (b.s - b.e)) ; res.x += (e.x - s.x) * t ; res.y += (e.y - s.y) * t ; return make_pair(res , 2) ; //有交點 } int relation(point p) //點和有向直線關係 { //正負需要考慮清楚 int c = sgn((p - s) ^ (e - s)) ;//直線認為是有向直線s->e,根據需求調整直線方向 if(c < 0) return 1 ; //1 在左側 else if(c > 0) return 2 ; //2 在右側 else return 3 ; //3 在直線上 } bool point_on_seg(point p) { return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s) * (p - e)) <= 0 ; } } ; bool can(int i , int j) { if(sgn(p[j].x - p[i].x) < 0) return 0 ; if(sgn(p[j].x - p[i].x) == 0 && sgn(p[j].y - p[i].y) < 0) return 0 ; return 1 ; } bool cmp(point a , point b) { db p = a.get_angle() , q = b.get_angle() ; if(sgn(p - q) != 0) return p < q ; return a.x < b.x ; } double cal(double x1 , double y1 , double x2 , double y2 , double x3 , double y3) { return fabs(x1 * y2 - y1 * x2 + x2 * y3 - y2 * x3 + x3 * y1 - y3 * x1) / 2 ; } db cal1(point p1 , point p2 , point p3) { return cal(p1.x , p1.y , p2.x , p2.y , p3.x , p3.y) ; } bool left(point p , point a , point b) { point p1 = p - a ; point p2 = b - a ; return sgn(p1 ^ p2) < 0 ; } bool In(point a , point b , point c , point k) { return left(k , a , b) && left(k , b , c) && left(k , c , a) ; } bool in(point o , point i , point j) { rep(k , 1 , n) if(In(o , i , j , p[k])) return 1 ; return 0 ; } bool ok(point o , point i , point j , point k) { point p1 = j - i ; point p2 = k - j ; return sgn(p1 ^ p2) > 0 ; } bool online(point o , point j) { if((o.dis(j)) > 0) { point p1 = j - o ; rep(s , 1 , n) { point p2 = p[s] - o ; if(sgn(o.dis(p[s])) > 0 && sgn(o.dis(j)) > 0 && sgn(p1 ^ p2) == 0 && sgn(o.dis(p[s]) - o.dis(j)) < 0) return 1 ; } } return 0 ; } db solve(int o) { //以(x_o,y_o)為左下角的點 db res = 0 ; rep(i , 0 , n) rep(j , 0 , n) dp[i][j] = -1e10 ; vector<point> v ; rep(i , 1 , n) if(i != o && can(o , i)) v.pb(p[i] - p[o]) ; sort(all(v) , cmp) ; reverse(all(v)) ; //for(auto u : v) ddebug(u.x , u.y) ; point oo = (point){0.0 , 0.0} ; rep(i , 0 , sz(v) - 1) rep(j , 0 , i - 1) { if(in(p[o] , p[o] + v[i] , p[o] + v[j])) continue ; db s = cal1(oo , v[i] , v[j]) ; dp[i][j] = s ; rep(k , 0 , j - 1) if(ok(oo , v[i] , v[j] , v[k]) && !online(p[o] , p[o] + v[j])) dp[i][j] = max(dp[i][j] , s + dp[j][k]) ; res = max(res , dp[i][j]) ; // ddebug(i , j) ; // debug(dp[i][j]) ; } return res ; } int main() { ios ; int T ; cin >> T ; while(T --) { cin >> n ; rep(i , 1 , n) cin >> p[i].x >> p[i].y ; db ans = 0 ; rep(i , 1 , n) ans = max(ans , solve(i)) ; print(1 , ans) ; } return 0 ; }
F - Heron and His Triangle
打表發現符合題意的滿足下式,其中表示第個滿足題意的
搞一個大數就好了。
不卡常,很友好。
#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 = 2e5 + 10 ; const int inf = 0x3f3f3f3f ; const double eps = 1e-6 ; struct bign { int d[105], len; void clean() { while(len > 1 && !d[len-1]) len--; } bign() { memset(d, 0, sizeof(d)); len = 1; } bign(int num) { *this = num; } bign(char* num) { *this = num; } bign operator = (const char* num){ memset(d, 0, sizeof(d)); len = strlen(num); for(int i = 0; i < len; i++) d[i] = num[len-1-i] - '0'; clean(); return *this; } bign operator = (int num){ char s[105]; sprintf(s, "%d", num); *this = s; return *this; } bign operator + (const bign& b){ bign c = *this; int i; for (i = 0; i < b.len; i++){ c.d[i] += b.d[i]; if (c.d[i] > 9) c.d[i]%=10, c.d[i+1]++; } while (c.d[i] > 9) c.d[i++]%=10, c.d[i]++; c.len = max(len, b.len); if (c.d[i] && c.len <= i) c.len = i+1; return c; } bign operator - (const bign& b){ bign c = *this; int i; for (i = 0; i < b.len; i++){ c.d[i] -= b.d[i]; if (c.d[i] < 0) c.d[i]+=10, c.d[i+1]--; } while (c.d[i] < 0) c.d[i++]+=10, c.d[i]--; c.clean(); return c; } bign operator * (const bign& b)const{ int i, j; bign c; c.len = len + b.len; for(j = 0; j < b.len; j++) for(i = 0; i < len; i++) c.d[i+j] += d[i] * b.d[j]; for(i = 0; i < c.len-1; i++) c.d[i+1] += c.d[i]/10, c.d[i] %= 10; c.clean(); return c; } bign operator / (const bign& b){ int i, j; bign c = *this, a = 0; for (i = len - 1; i >= 0; i--) { a = a*10 + d[i]; for (j = 0; j < 10; j++) if (a < b*(j+1)) break; c.d[i] = j; a = a - b*j; } c.clean(); return c; } bign operator % (const bign& b){ int i, j; bign a = 0; for (i = len - 1; i >= 0; i--) { a = a*10 + d[i]; for (j = 0; j < 10; j++) if (a < b*(j+1)) break; a = a - b*j; } return a; } bign operator += (const bign& b){ *this = *this + b; return *this; } bool operator <(const bign& b) const{ if(len != b.len) return len < b.len; for(int i = len-1; i >= 0; i--) if(d[i] != b.d[i]) return d[i] < b.d[i]; return false; } bool operator >(const bign& b) const{return b < *this;} bool operator<=(const bign& b) const{return !(b < *this);} bool operator>=(const bign& b) const{return !(*this < b);} bool operator!=(const bign& b) const{return b < *this || *this < b;} bool operator==(const bign& b) const{return !(b < *this) && !(b > *this);} string str() const{ char s[maxn]={}; for(int i = 0; i < len; i++) s[len-1-i] = d[i]+'0'; return s; } } bb[105] ; istream& operator >> (istream& in, bign& x) { string s; in >> s; x = s.c_str(); return in; } ostream& operator << (ostream& out, const bign& x) { out << x.str(); return out; } int main() { ios ; bb[1] = 4 ; bb[2] = 14 ; rep(i , 3 , 55) bb[i] = bb[i - 1] * 4 - bb[i - 2] ; int T ; cin >> T ; while(T --) { bign a ; cin >> a ; rep(i , 1 , 55) if(a <= bb[i]) { cout << bb[i] << '\n' ; break ; } } return 0 ; }
G - Infinite Fraction Path
容易發現在起點固定時的路徑是一個非迴圈串和無限迴圈串構成的。
發現在最壞情況下(即是素數),非迴圈串和個迴圈節的長度不超過。
然後暴力亂搞就行了。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5, lim = 300;
int n, d[N], nxt[N], p[N], kase;
char s[N];
void Max(int &x, int y)
{
int a = x, b = y, cnt = 1;
while(d[a]==d[b])
{
a = nxt[a], b = nxt[b];
++cnt;
if(cnt>=min(lim, n)) break;
}
if(d[a]<d[b]) x = y;
}
void solve()
{
scanf("%d%s", &n, s);
for(int i=0; i<n; i++) d[i] = s[i] - '0';
for(int i=0; i<n; i++) p[i] = i, nxt[i] = (1ll*i*i+1)%n;
random_shuffle(p, p+n);
int mx = p[0];
for(int i=1; i<n; i++) Max(mx, p[i]);
printf("Case #%d: ", ++kase);
for(int i=0; i<n; i++)
{
printf("%d", d[mx]);
mx = nxt[mx];
}
puts("");
}
int main()
{
int _; scanf("%d", &_);
while(_--) solve();
return 0;
}
I - Little Boxes
特判超過的測例就可以了。
#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 = 2e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ;
unsigned long long a[10] ;
int main()
{
ios ;
int T ;
cin >> T ;
while(T --)
{
unsigned long long ans = 0 ;
rep(i , 1 , 4) cin >> a[i] ;
if(a[1] == (1ll << 62) && a[2] == (1ll << 62) && a[3] == (1ll << 62) && a[4] == (1ll << 62))
{
cout << "18446744073709551616" << '\n' ;
continue ;
}
else rep(i , 1 , 4) ans += a[i] ;
cout << ans << '\n' ;
}
return 0 ;
}
K - Rabbits
答案 = 相鄰兩個兔子間空位長度的和 - min(最左邊兔子和它右邊的相鄰兔子之間的空位長度,最右邊兔子和它左邊的相鄰兔子之間的空位長度)
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n; scanf("%d", &n);
vector<int> a(n);
for(int &x : a) scanf("%d", &x);
int ans = 0;
for(int i=1; i<n; i++)
ans += a[i] - a[i-1] - 1;
printf("%d\n", ans-min(a[1]-a[0]-1, a[n-1]-a[n-2]-1));
}
int main()
{
int _; scanf("%d", &_);
while(_--) solve();
return 0;
}
L - Tree
我們訓練的時候是用拓撲做的。賽後發現有個簡單做法,如果一條邊的左右兩端都有大於等於個節點,那麼這條邊就可以被計數一次。
#include<bits/stdc++.h>
using namespace std;
#define M 200005
vector<int>G[M];
int stk[M],top,sz[M],du[M];
void solve(){
int n,k;
scanf("%d %d",&n,&k);
top=0;
for(int i=1;i<=n;i++){
G[i].clear();
sz[i]=1;
du[i]=0;
}
for(int i=1;i<n;i++){
int a,b;
scanf("%d %d",&a,&b);
G[a].push_back(b);
G[b].push_back(a);
du[a]++;
du[b]++;
}
int dlt=0;
for(int i=1;i<=n;i++){
if(du[i]==1)stk[++top]=i;
}
for(int i=1;i<=top;i++){
int x=stk[i];
if(sz[x]>=k)continue;
dlt++;
for(int j=0;j<G[x].size();j++){
int y=G[x][j];
if(du[y]==1)continue;
du[y]--;
sz[y]+=sz[x];
if(du[y]==1)stk[++top]=y;
}
}
printf("%d\n",n-1-dlt);
}
int main(){
int T;
scanf("%d",&T);
while(T--)solve();
return 0;
}
M - Wandering Robots
根據樣例的猜個結論就好了。
每個點賦權值,權值大小是當前點可以被幾個相鄰點所到達(包括自身)。
權值和的比值就是答案。
#include<bits/stdc++.h>
using namespace std;
#define M 200005
map<pair<int,int>,int>mp;
int n,k;
bool check(int x,int y){
return x>=0&&x<n&&y>=0&&y<n;
}
int kase;
void solve(){
scanf("%d %d",&n,&k);
int ans1=(5*n*n+n-4)/2,ans2=5*n*n-4*n;
mp.clear();
for(int i=1;i<=k;i++){
int a,b;
scanf("%d %d",&a,&b);
int cnt=0;
if(check(a-1,b)&&!mp[make_pair(a-1,b)]){
cnt++;
ans2--;
if((a-1)+b>=n-1)ans1--;
}
if(check(a+1,b)&&!mp[make_pair(a+1,b)]){
cnt++;
ans2--;
if((a+1)+b>=n-1)ans1--;
}
if(check(a,b-1)&&!mp[make_pair(a,b-1)]){
cnt++;
ans2--;
if(a+(b-1)>=n-1)ans1--;
}
if(check(a,b+1)&&!mp[make_pair(a,b+1)]){
cnt++;
ans2--;
if(a+(b+1)>=n-1)ans1--;
}
ans2-=cnt+1;
if(a+b>=n-1)ans1-=cnt+1;
mp[make_pair(a,b)]=1;
}
int Gcd=__gcd(ans1,ans2);
printf("Case #%d: %d/%d\n",++kase,ans1/Gcd,ans2/Gcd);
}
int main(){
int T;
scanf("%d",&T);
while(T--)solve();
return 0;
}