1. 程式人生 > 其它 >2016-2017 ACM-ICPC Southwestern European Regional Programming Contest (SWERC 2016) 部分題解

2016-2017 ACM-ICPC Southwestern European Regional Programming Contest (SWERC 2016) 部分題解

比賽連結

Problem A: Within Arm's Reach

留坑。

Problem B: Bribing Eve

看作點積相乘。把w看作單位向量,w*x_1<w*x_i有一個w的取值範圍,得到n-1個符合這個式子的取值範圍,對取值範圍求交。可以得到排名的最大值和最小值。

#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

dp[i][j][k]表示第一個人和第二個人共有的數字出現i個,第一個人專屬的數字出現j個,第二個人專屬的數字出現k個。

#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

dp[i][j][k]表示長度是i的字串中含有j個字母且位於字典圖上第k個節點的方案數。

下面分析怎麼統計答案。

  • 至少出現一個大寫字母和一個小寫字母。但是dp狀態不用區分大小寫。只要去掉全部大寫和全部小寫的兩種情況即可。
  • 至少出現一個數字。只要j<i即可。
  • 有些數字可以被當作字母。數字當作對應的字元在字典圖上轉移即可。
#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

按照rank​​​​​​​排序後樹狀陣列維護答案即可。

#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

13意義下的高斯消元。然後跑floyd

#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 ;
}