zoukankan      html  css  js  c++  java
  • 【AtCoder】CODE FESTIVAL 2017 Final

    A - AKIBA

    模拟即可

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 200005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    int N;
    string s,tar = "AKIHABARA";
    bool Solve() {
        cin >> s;
        if(s.length() > 9) {
    	return false;
        }
        for(int i = 0 ; i < 9 ; ++i) {
    	if(s.length() <= i) s += "A";
    	if(s[i] != tar[i]) {
    	    if(tar[i] == 'A') {
    		s.insert(i,1,'A');
    	    }
    	    else return false;
    	}
        }
        if(s != tar) return false;
        return true;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        if(Solve()) puts("YES");
        else puts("NO");
        return 0;
    }
    

    B - Palindrome-phobia

    题解

    abc出现次数的最大值和最小值相差不超过1

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 100005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    char s[MAXN];
    int cnt[4];
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        scanf("%s",s + 1);
        int N = strlen(s + 1);
        for(int i = 1 ; i <= N ; ++i) {
    	cnt[s[i] - 'a']++;
        }
        int minn = min(min(cnt[0],cnt[1]),cnt[2]);
        int mmax = max(max(cnt[0],cnt[1]),cnt[2]);
        if(mmax - minn <= 1) puts("YES");
        else puts("NO");
        return 0;
    }
    

    C - Time Gap

    题解

    显然每个时间有超过三个人答案一定是0
    之后对于每个时间里的人进行枚举是d还是24 - d即可

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 100005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    int cnt[15];
    int N,D[55];
    bool vis[25],mark[15],c[25];
    void Init() {
        read(N);
        for(int i = 1 ; i <= N ; ++i) {read(D[i]);cnt[D[i]]++;}
    }
    void Solve() {
        if(cnt[0] || cnt[12] >= 2) {puts("0");return;}
        int S = 0;
        int ans = 0,d = 12;
        for(int i = 1 ; i <= 12 ; ++i) {
    	
    	if(cnt[i] > 2) {puts("0");return;}
    	if(cnt[i] == 2) {
    	    vis[i] = 1;vis[24 - i] = 1;
    	}
    	else if(cnt[i]) {d = min(d,i);S |= 1 << i - 1;mark[i] = 1;}
        }
        for(int T = S ; ; T = S & (T - 1)) {
    	memcpy(c,vis,sizeof(vis));
    	for(int i = 1 ; i <= 12 ; ++i) {
    	    if(mark[i]) {
    		if(T >> (i - 1) & 1) c[24 - i] = 1;
    		else c[i] = 1;
    	    }
    	}
    	int pre = 0,t = d;
    	for(int i = 1 ; i <= 24 ; ++i) {
    	    if(c[i]) {t = min(t,i - pre);pre = i;}
    	}
    	ans = max(ans,t);
    	if(T == 0) break;
        }
        out(ans);enter;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Init();
        Solve();
        return 0;
    }
    

    D - Zabuton

    题解

    如果两个相邻的点(a)(b),前面的前缀和是(x)
    那么我们有
    (min(H[a],H[b] - P[a]) >= x)
    (min(H[b],H[a] - P[b]) >= x)
    我们希望允许尽量宽松的x
    如果(min(H[a],H[b] - P[a]) < min(H[b],H[a] - P[b]))的话,a在前,否则b在前

    排序后进行(dp[i][j])表示到第(i)个点选了(j)个点最小前缀和即可,用前缀min优化更新

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 5005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    int H[MAXN],P[MAXN],id[MAXN],N;
    int64 sum[MAXN];
    void Solve() {
        read(N);
        for(int i = 1 ; i <= N ; ++i) {
    	read(H[i]);read(P[i]);id[i] = i;
        }
        sort(id + 1,id + N + 1,[](int a,int b) {
    	    if(H[a] < H[b] - P[a]) return true;
    	    if(H[b] < H[a] - P[b]) return false;
    	    return H[a] + P[a] < H[b] + P[b];
        });
        for(int i = 1 ; i <= N ; ++i) sum[i] = 1e18;
        for(int i = 1 ; i <= N ; ++i) {
    	int u = id[i];
    	for(int j = N ; j >= 1 ; --j) {
    	    if(H[u] >= sum[j - 1]) {
    		sum[j] = min(sum[j],sum[j - 1] + P[u]);
    	    }
    	}
        }
        for(int i = N ; i >= 1 ; --i) {
    	if(sum[i] != 1e18) {out(i);enter;return;}
        }
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Solve();
        return 0;
    }
    

    E - Combination Lock

    题解

    处理成差分,对称位置的差分和为0
    例如abcba
    可以得到的差分是
    相当于数列
    0123210
    111-1-1-1

    而有区间加呢,相当于在前面打了一个+1,后面打了一个-1
    我们把这两个位置连边
    并且给所有对称位置连边

    合法的情况仅当一个联通块和为0

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 100005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    char s[MAXN];
    int a[MAXN],N,M;
    struct node {
        int to,next;
    }E[MAXN * 10];
    int head[MAXN],sumE,sum;
    bool vis[MAXN];
    void add(int u,int v) {
        E[++sumE].to = v;
        E[sumE].next = head[u];
        head[u] = sumE;
    }
    void Init() {
        scanf("%s",s + 1);
        N = strlen(s + 1);
        s[0] = 'a';s[N + 1] = 'a';
        for(int i = 1 ; i <= N + 1; ++i) a[i] = (s[i] - s[i - 1] + 26) % 26;
        read(M);
        int L,R;
        for(int i = 1 ; i <= M ; ++i) {
    	read(L);read(R);
    	add(L,R + 1);add(R + 1,L);
        }
        for(int i = 1 ; i <= N + 1 ; ++i) {
    	add(i,N + 2 - i);
        }
    }
    void dfs(int u) {
        vis[u] = 1;
        sum = (sum + a[u]) % 26;
        for(int i = head[u] ; i ; i = E[i].next) {
    	int v = E[i].to;
    	if(!vis[v]) {
    	    dfs(v);
    	}
        }
    }
    void Solve() {
        bool flag = 1;
        for(int i = 1 ; i <= N + 1 ; ++i) {
    	if(!vis[i]) {
    	    sum = 0;
    	    dfs(i);
    	    if(sum != 0) {flag = 0;break;} 
    	}
        }
        if(flag) puts("YES");
        else puts("NO");
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Init();
        Solve();
        return 0;
    }
    

    F - Distribute Numbers

    题解

    给第一行填上1 - K,很容易发现N的下界是(K(K - 1) + 1)
    然后给第2到(K)行全填上1,(K + 1)(2K)全填上2,以此类推
    然后我们再把剩余未分配的数全填到(2)行到(K)行大小为(K - 1)的正方形矩阵里
    然后我们要把这个正方形矩阵划分成(K - 1)种,每种(K - 1)条不相交的链
    (K - 1)选一个质数可以完成这个操作

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 5005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    vector<int> v[1500];
    int N = 1407;
    int K = 38;
    int a[45][45];
    void Solve() {
        for(int i = 1 ; i <= K ; ++i) v[1].pb(i);
        int t = 0;
        for(int i = 2 ; i <= N ; i += (K - 1)) {
    	++t;
    	for(int j = i ; j <= i + (K - 1) - 1 ; ++j) v[j].pb(t);
        }
        t = K;
        for(int i = 0 ; i < K - 1 ; ++i) {
    	for(int j = 0 ; j < K - 1 ; ++j) {
    	    a[i][j] = ++t;
    	    v[i + 2].pb(t);
    	}
        }
        t = 0;
        for(int i = K + 1 ; i <= N ; i += (K - 1)) {
    	for(int j = 0 ; j < K - 1 ; ++j) {
    	    int p = j;
    	    for(int h = 0 ; h < K - 1 ; ++h) {
    		v[i + j].pb(a[h][p]);
    		p = (p + t) % (K - 1);
    	    }
    	}
    	++t;
        }
        out(N);space;out(K);enter;
        for(int i = 1 ; i <= N ; ++i) {
    	for(auto b : v[i]) {
    	    out(b);space;
    	}
    	enter;
        }
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Solve();
        return 0;
    }
    

    G - Mancala

    题解

    很容易想到(sum[i][j])表示第(i)个点还需要后面给加(j)次的价值总和,(cnt[i][j])表示第(i)个点还需要后面给加(j)次的方案数

    由于能用到的状态不多,可以记忆化搜索,答案是(dp[N][0])

    转移就枚举这第(i)位放了(p)个,需要后面加(d)
    简单列个方程可以知道后面需要加(d + lfloor frac{d + p}{i} floor)
    然后加上(cnt[i - 1][d + lfloor frac{d + p}{i} floor] * (p - frac{d + p}{i}))

    边界是对于1,(sum[1][d] = frac{(K - 1) * (K + 2)}{2} - 2 * d,cnt[1][d] = K + 1)
    因为1如果不满的话加多少都会扔掉

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 100005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    const int MOD = 1000000007;
    bool vis[105][100005];
    int sum[105][100005],cnt[105][100005];
    int N,K;
    int inc(int a,int b) {
        return a + b >= MOD ? a + b - MOD : a + b;
    }
    int mul(int a,int b) {
        return 1LL * a * b % MOD;
    }
    void update(int &x,int y) {
        x = inc(x,y);
    }
    int fpow(int x,int c) {
        int res = 1,t = x;
        while(c) {
    	if(c & 1) res = mul(res,t);
    	t = mul(t,t);
    	c >>= 1;
        }
        return res;
    }
    void dfs(int p,int d) {
        if(vis[p][d]) return;
        vis[p][d] = 1;
        if(p == 1) {
    	cnt[p][d] = K + 1;sum[p][d] = (mul(2,MOD - d) + (K + 2) * (K - 1) / 2) % MOD;
    	return ;
        }
        for(int i = 0 ; i < p ; ++i) {
    	if(i > K) break;
    	dfs(p - 1,d + (d + i) / p);
    	update(cnt[p][d],cnt[p - 1][d + (d + i) / p]);
    	update(sum[p][d],inc(sum[p - 1][d + (d + i) / p] ,mul(cnt[p - 1][d + (d + i) / p] ,inc(i, MOD - (d + i) / p))));
        }
        if(p <= K) {
    	dfs(p - 1,d + 1 + d / p);
    	update(cnt[p][d],cnt[p - 1][d + 1 + d / p]);
    	update(sum[p][d],inc(sum[p - 1][d + 1 + d / p], mul(cnt[p - 1][d + 1 + d / p],inc(p, MOD - d / p - 1))));
        }
        for(int i = p + 1 ; i <= K ; ++i) {
    	dfs(p - 1,d);
    	update(cnt[p][d],cnt[p - 1][d]);
    	update(sum[p][d],inc(sum[p - 1][d],mul(cnt[p - 1][d], i)));
        }
    }
    void Solve() {
        read(N);read(K);
        dfs(N,0);
        out(sum[N][0]);enter;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Solve();
        return 0;
    }
    

    H - Poor Penguin

    题解

    题解里的图画的挺好的

    就是我们考虑把大矩形分成小矩形的代价是什么
    例如一个矩形([lx,ly,rx,ry])
    我把它从([lx,ly,i,j])里分出来
    需要就是把
    ([lx,ry + 1,rx,j])([rx + 1,ly,i,ry])里所有的障碍都扣去

    然后我们对于每个包含p点的矩形,计算使得从P开始左上右上左下右下的一个角都扣去的最小代价

    其实这是(n^6)的,怂的一批,不过记忆化搜索加上一点剪枝和AtCoder强大的评测机,好像还是不到1s的样子

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 100005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    bool vis[45][45][45][45];
    int dp[45][45][45][45];
    char s[45][45];
    int H,W,sum[45][45];
    void Init() {
        read(H);read(W);
        for(int i = 1 ; i <= H ; ++i) scanf("%s",s[i] + 1);
    }
    int query(int lx,int ly,int rx,int ry) {
        return sum[rx][ry] + sum[lx - 1][ly - 1] - sum[rx][ly - 1] - sum[lx - 1][ry];
    }
    int dfs(int lx,int ly,int rx,int ry) {
        if(lx == 1 && ly == 1 && rx == H  && ry == W) return 0;
        if(vis[lx][ly][rx][ry]) return dp[lx][ly][rx][ry];
        int res = 100000;
        for(int i = rx + 1 ; i <= H ; ++i) {
    	for(int j = ry + 1 ; j <= W ; ++j) {
    	    int t = query(lx,ry + 1,rx,j) + query(rx + 1,ly,i,ry);
    	    if(t >= res) break;
    	    res = min(t + dfs(lx,ly,i,j),res);
    	}
        }
        for(int i = rx + 1 ; i <= H ; ++i) {
    	for(int j = ly - 1 ; j >= 1 ; --j) {
    	    int t = query(lx,j,rx,ly - 1) + query(rx + 1,ly,i,ry);
    	    if(t >= res) break;
    	    res = min(t + dfs(lx,j,i,ry),res);
    	}
        }
        for(int i = lx - 1 ; i >= 1 ; --i) {
    	for(int j = ly - 1 ; j >= 1 ; --j) {
    	    int t = query(i,ly,lx - 1,ry) + query(lx,j,rx,ly - 1);
    	    if(t >= res) break;
    	    res = min(t + dfs(i,j,rx,ry),res);
    	}
        }
        for(int i = lx - 1 ; i >= 1 ; --i) {
    	for(int j = ry + 1 ; j <= W ; ++j) {
    	    int t = query(i,ly,lx - 1,ry) + query(lx,ry + 1,rx,j);
    	    if(t >= res) break;
    	    res = min(t + dfs(i,ly,rx,j),res);
    	}
        }
        vis[lx][ly][rx][ry] = 1;
        return dp[lx][ly][rx][ry] = res;
    }
    void Solve() {
        pii p;
        for(int i = 1 ; i <= H ; ++i) {
    	for(int j = 1 ; j <= W ; ++j) {
    	    if(s[i][j] == 'P') p = mp(i,j);
    	    if(s[i][j] == '#') sum[i][j]++;
    	    sum[i][j] = sum[i][j] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
    	}
        }
        int ans = 100000;
        for(int i = 1 ; i <= H ; ++i) {
    	for(int j = 1 ; j <= W ; ++j) {
    	    for(int k = i ; k <= H ; ++k) {
    		for(int h = j ; h <= W ; ++h) {
    		    if(p.fi >= i && p.fi <= k && p.se >= j && p.se <= h) {
    			int t = query(i,j,p.fi,p.se);
    			t = min(t,query(p.fi,p.se,k,h));
    			t = min(t,query(i,p.se,p.fi,h));
    			t = min(t,query(p.fi,j,k,p.se));
    			if(t >= ans) continue;
    			ans = min(t + dfs(i,j,k,h),ans);
    		    }
    		}
    	    } 
    	}
        }
        out(ans);enter;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Init();
        Solve();
        return 0;
    }
    

    I - Full Tournament

    题解

    观察一下可得
    (a_{i} < a_{i | 2^{k}})
    我们把这个当做一条边,会连出一个dag,相当于给出拓扑序的一部分回复全部
    可以对每个点求一个放的位置取值范围,如果这个点固定了就把左右端点都设成那个值

    然后每次选一个右端点最小的放进去,不存在或右端点不合法就是无解了

    恢复成原来的观察一下就是二进制反转

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 100005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    int A[(1 << 18) + 5],N;
    vector<int> e[2][(1 << 18) + 5];
    int d[2][(1 << 18) + 5];
    int L[(1 << 18) + 5],R[(1 << 18) + 5];
    int que[2][(1 << 18) + 5],ql,qr,ans[(1 << 18) + 5];
    vector<int> st[(1 << 18) + 5],ed[(1 << 18) + 5];
    struct cmp {
        bool operator () (const int &a,const int &b) const {
    	return R[a] < R[b] || (R[a] == R[b] && a < b);
        }
    };
    set<int,cmp> S;
    void Init() {
        read(N);
        for(int i = 0 ; i < (1 << N) ; ++i) {
    	read(A[i]);--A[i];
    	for(int j = 0 ; j < N ; ++j) {
    	    if(!(i >> j & 1)) {
    		e[0][i].pb(i + (1 << j));
    		d[0][i + (1 << j)]++;
    		e[1][i + (1 << j)].pb(i);
    		d[1][i]++;
    	    }
    	}
    	L[i] = 0;R[i] = (1 << N) - 1;
        }
    }
    void Solve() {
        L[0] = R[0] = 0;
        for(int i = 0 ; i < (1 << N) ; ++i) {
            if(A[i] != -1) L[i] = A[i];
            int u = i;
        	for(auto v : e[0][u]) {
        	    L[v] = max(L[v],L[u] + 1);
        	}
        }
        R[(1 << N) - 1] = L[(1 << N) - 1] = (1 << N) - 1;
    
        for(int i = (1 << N) - 1 ; i >= 0 ; --i) {
            if(A[i] != -1) R[i] = A[i];
        	int u = i;
        	for(auto v : e[1][u]) {
        	    R[v] = min(R[u] - 1,R[v]);
        	}
        }
        for(int i = 0 ; i < (1 << N) ; ++i) {
        	st[L[i]].pb(i);
        }
        for(int i = 0 ; i < (1 << N) ; ++i) {
            for(auto t : st[i]) S.insert(t);
            if(S.empty()) {puts("NO");return;}
            int t = *S.begin();
            S.erase(S.begin());
            if(R[t] < i) {puts("NO");return;}
            ans[t] = i;
        }
    
        puts("YES");
        for(int i = 1 , j = (1 << N - 1) ; i < (1 << N) - 1 ; ++i) {
        	if(i < j) swap(ans[i],ans[j]);
        	int k = (1 << N - 1);
        	while(j >= k) {
        	    j -= k;
        	    k >>= 1;
        	}
        	j += k;
        }
        for(int i = 0 ; i < (1 << N) ; ++i) {
        	out(ans[i] + 1);space;
        }
        enter;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Init();
        Solve();
        return 0;
    }
    

    J - Tree MST

    题解

    好像直接点分可以爆艹
    就是考虑点分每个点向别的子树里距离最短的点连边即可
    然后直接kruskal

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 200005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    int N;
    int64 X[MAXN];
    struct node {
        int to,next;int64 val;
    }E[MAXN * 2];
    int head[MAXN],sumE;
    int que[MAXN],ql,qr,fa[MAXN],siz[MAXN],son[MAXN];
    int64 dis[MAXN];
    pair<int64,int> pre[MAXN],suf[MAXN];
    vector<int> ver[MAXN];
    bool vis[MAXN];
    struct Enode {
        int u,v;int64 c;
        friend bool operator < (const Enode &a,const Enode &b) {
    	return a.c < b.c;
        }
    }edge[MAXN * 30];
    int tot;
    void add(int u,int v,int64 c) {
        E[++sumE].to = v;
        E[sumE].next = head[u];
        E[sumE].val = c;
        head[u] = sumE;
    }
    void Init() {
        read(N);
        for(int i = 1 ; i <= N ; ++i) read(X[i]);
        int a,b;int64 c;
        for(int i = 1 ; i < N ; ++i) {
    	read(a);read(b);read(c);
    	add(a,b,c);add(b,a,c);
        }
    }
    int getfa(int u) {
        return fa[u] == u ? u : fa[u] = getfa(fa[u]);
    }
    int Calc(int st) {
        que[ql = qr = 1] = st;
        fa[st] = 0;dis[st] = 0;
        while(ql <= qr) {
    	int u = que[ql++];
    	siz[u] = 1;son[u] = 0;
    	for(int i = head[u] ; i ; i = E[i].next) {
    	    int v = E[i].to;
    	    if(v != fa[u] && !vis[v]) {
    		que[++qr] = v;
    		fa[v] = u;
    	    }
    	}
        }
        int res = que[qr];
        for(int i = qr ; i >= 1 ; --i) {
    	int u = que[i];
    	if(fa[u]) {son[fa[u]] = max(son[fa[u]],siz[u]);siz[fa[u]] += siz[u];}
    	son[u] = max(son[u],qr - siz[u]);
    	if(son[u] < son[res]) res = u; 
        }
        return res;
    }
    void dfs(int u) {
        int G = Calc(u);
        vis[G] = 1;
        int cnt = 0;
        dis[G] = 0;
        for(int i = head[G] ; i ; i = E[i].next) {
    	int v = E[i].to;
    	if(!vis[v]) {
    	    ++cnt;
    	    ver[cnt].clear();
    	    ver[cnt].pb(v);
    	    fa[v] = G;dis[v] = dis[G] + E[i].val;
    	    int s = 0;pair<int64,int> val = mp(dis[v] + X[v],v);
    	    while(s < ver[cnt].size()) {
    		int n = ver[cnt][s];++s;
    		for(int k = head[n] ; k ; k = E[k].next) {
    		    int h = E[k].to;
    		    if(!vis[h] && h != fa[n]) {
    			fa[h] = n;
    			ver[cnt].pb(h);
    			dis[h] = dis[n] + E[k].val;
    			val = min(mp(dis[h] + X[h],h),val);
    		    }
    		}
    	    }
    	    pre[cnt] = val;
    	    suf[cnt] = val;
    	}
        }
        pre[0] = mp(1e18,0);
        for(int i = 1 ; i <= cnt ; ++i) pre[i] = min(pre[i - 1],pre[i]);
        suf[cnt + 1] = mp(1e18,0);
        for(int i = cnt ; i >= 1 ; --i) suf[i] = min(suf[i + 1],suf[i]);
        for(int i = 1 ; i <= cnt ; ++i) {
    	pair<int64,int> t = min(pre[i - 1],suf[i + 1]);
    	t = min(t,mp(X[G],G));
    	for(auto v : ver[i]) {
    	    edge[++tot] = (Enode){v,t.se,t.fi + dis[v] + X[v]};
    	}
        }
        edge[++tot] = (Enode){G,pre[cnt].se,pre[cnt].fi + dis[G] + X[G]};
        for(int i = head[G] ; i ; i = E[i].next) {
    	int v = E[i].to;
    	if(!vis[v]) dfs(v);
        }
    }
    void Solve() {
        dfs(1);
        sort(edge + 1,edge + tot + 1);
        for(int i = 1 ; i <= N ; ++i) fa[i] = i;
        int64 ans = 0;
        int cnt = 0;
        for(int i = 1 ; i <= tot ; ++i) {
    	if(getfa(edge[i].u) != getfa(edge[i].v)) {
    	    ans += edge[i].c;
    	    fa[getfa(edge[i].u)] = getfa(edge[i].v);
    	    ++cnt;
    	    if(cnt == N - 1) break;
    	}
        }
        out(ans);enter;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Init();
        Solve();
        return 0;
    }
    
  • 相关阅读:
    PY个树状数组
    PY 个板子计划【雾
    PY个欧拉筛
    【NOI2007】项链工厂 ——老题新做.jpg
    Min-Max 容斥的证明
    51nod 1963 树上Nim
    ●BZOJ 3566 [SHOI2014]概率充电器
    ●BZOJ 3640 JC的小苹果
    ●BZOJ 1444 [Jsoi2009]有趣的游戏
    ●Joyoi Dotp 驱逐猪猡
  • 原文地址:https://www.cnblogs.com/ivorysi/p/10185773.html
Copyright © 2011-2022 走看看