【AtCoder】AtCoder Petrozavodsk Contest 001

A - Two Integers

如果\(X\)\(Y\)的倍数的话不存在
可以输出\(X \cdot (\frac{Y}{gcd(X,Y)} - 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('\n')
#define space putchar(' ')
#define MAXN 100005
#define eps 1e-8
//#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);
}
int64 X,Y;
int64 gcd(int64 a,int64 b) {
    return b == 0 ? a : gcd(b,a % b);
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    read(X);read(Y);
    if(X % Y == 0) {puts("-1");return 0;}
    int64 t = Y / gcd(X,Y);
    out(X * (t - 1));enter;
}

B - Two Arrays

a<b的话,这个位置一定会合法,如果a > b的话不可能再小于b了,所以我们看看在用所有位置的a增加不超过b的位置的情况下,能一共给b增加多少,如果这个增加数大于所有a > b的a和b差值的和就合法

代码

#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('\n')
#define space putchar(' ')
#define MAXN 100005
#define eps 1e-8
//#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[MAXN],b[MAXN],N;
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    read(N);
    for(int i = 1 ; i <= N ; ++i) read(a[i]);
    for(int i = 1 ; i <= N ; ++i) read(b[i]);
    int64 all = 0;
    for(int i = 1 ; i <= N ; ++i) {
        if(a[i] > b[i]) all += a[i] - b[i];
        else all -= (b[i] - a[i]) / 2;
    }
    if(all > 0) puts("No");
    else puts("Yes");
}

C - Vacant Seat

二分
我知道了最左和最右两边的后,如果这个位置和两边都是奇数区间的话
例如
1 *** 0 *** 0
那么空位在左边
如果两边都是偶数长的话
例如
1 **** 0 **** 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('\n')
#define space putchar(' ')
#define MAXN 100005
#define eps 1e-8
//#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;
int num[2];
char s[15];
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    read(N);
    out(0);enter;fflush(stdout);
    scanf("%s",s + 1);

    if(s[1] == 'F') num[0] = 1;
    else if(s[1] == 'M') num[0] = 0;
    else return 0;
    out(N - 1);enter;fflush(stdout);
    scanf("%s",s + 1);

    if(s[1] == 'F') num[1] = 1;
    else if(s[1] == 'M') num[1] = 0;
    else return 0;
    int L = 1,R = N - 2;
    while(L < R) {
        int mid = (L + R) >> 1;
        out(mid);enter;fflush(stdout);
        scanf("%s",s + 1);

        if(s[1] == 'V') return 0;
        int a;
        if(s[1] == 'M') a = 0;
        else a = 1;
        int t = a ^ ((mid - L) & 1);
        if(t == num[0]) {R = mid - 1;num[1] = a;}
        else {L = mid + 1;num[0] = a;}
    }
    out(L);enter;fflush(stdout);scanf("%s",s + 1);
    return 0;
}

D - Forest

把每个点排序,如果加进来的点所在的联通块没有点,那么把这个点放进队列里,如果还有别的联通块里的点,就把这个点和别的联通块连一条边,然后删掉这个点,把这两个联通块用并查集连在一起

如果没有别的联通块里的点,那么就把这个点扔进队列,把队列打上标记,如果之后还有别的联通块的点来,然后取出队列里这个联通块中的点和新加的点连一条边
直到队列里只有一个点就把标记删除

如果只有两个联通块,队列没有标记,且有多于两个点,这两个点之间连一条边退出就好了

保证每次都是取了最小的两个点连边(第一种操作也是保证取了最小的因为我保证了在队列里同联通块的点一定会被用,所以这个点也可用)

代码

#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('\n')
#define space putchar(' ')
#define MAXN 100005
#define eps 1e-8
//#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,M,id[MAXN],conn;
int64 a[MAXN];
int fa[MAXN];
bool vis[MAXN];
int que[MAXN],ql,qr;
bool flag = 0;
bool cmp(int s,int t) {
    return a[s] < a[t];
}
int getfa(int x) {
    return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
void Solve() {
    read(N);read(M);
    for(int i = 1 ; i <= N ; ++i) {read(a[i]);id[i] = i;}
    sort(id + 1,id + N + 1,cmp);
    for(int i = 1 ; i <= N ; ++i) fa[i] = i;
    int x,y;
    conn = N;
    for(int i = 1 ; i <= M ; ++i) {
        read(x);read(y);
        ++x;++y;
        conn--;
        fa[getfa(x)] = getfa(y);
    }
    if(conn == 1) {puts("0");return;}
    ql = 1,qr = 0;
    int64 ans = 0;
    for(int i = 1 ; i <= N ; ++i) {
        int u = id[i];
        if(flag) {
            if(getfa(u) == getfa(que[ql])) que[++qr] = u;
            else {
                ans += a[que[ql]] + a[u];
                --conn;
                fa[getfa(u)] = getfa(que[ql]);
                ql++;
                if(qr == ql) flag = 0;
            }
        }
        else {
            if(vis[getfa(u)]) {
                if(ql == qr) {flag = 1;que[++qr] = u;}
                else if(getfa(que[ql]) == getfa(u)) {
                    ans += a[que[ql]] + a[que[ql + 1]];
                    --conn;
                    fa[getfa(que[ql + 1])] = getfa(que[ql]);
                    ql += 2;
                    que[++qr] = u;
                }
                else {
                    ans += a[u] + a[que[ql]];
                    --conn;
                    fa[getfa(que[ql])] = getfa(u);
                    ql++;
                }

            }
            else {
                que[++qr] = u;vis[getfa(u)] = 1;
            }
        }
        if(conn == 1) break;
        if(conn == 2 && qr - ql + 1 >= 2 && !flag) {
            ans += a[que[ql]] + a[que[ql + 1]];
            --conn;
            break;
        }
    }
    if(conn != 1) {puts("Impossible");}
    else {out(ans);enter;}
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}

E - Antennas on Tree

简单的树dp

\(dp[v]\)为以0为根时\(v\)点子树里最少需要选择的点

\(dp[u] = \sum_{v\in son(u)} dp[v]\)
计算一个\(cnt\)\(v\)\(dp[v]\)不为0的个数
$dp[u] += max(0,son - 1 - cnt) $

然后就是换根了,不难换具体看代码吧

题解

#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('\n')
#define space putchar(' ')
#define eps 1e-8
#define MAXN 100005
#define mo 974711
//#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);
}

struct node {
    int to,next;
}E[MAXN * 2];
int head[MAXN],sumE,N,ans;
int dp[MAXN],fr[MAXN];
void add(int u,int v) {
    E[++sumE].to = v;
    E[sumE].next = head[u];
    head[u] = sumE;
}
void dfs1(int u,int fa) {
    int son = 0,cnt = 0;
    for(int i = head[u] ; i ; i = E[i].next) {
	int v = E[i].to;
	if(v != fa) {
	    ++son;
	    dfs1(v,u);
	    if(dp[v]) ++cnt;
	    dp[u] += dp[v];
	}
    }
    if(cnt < son - 1) dp[u] += son - 1 - cnt;
}
void dfs2(int u,int fa) {
    int sum = 0,son = 0,cnt = 0;
    if(fa != -1) {
	sum = 0,son = 0,cnt = 0;
	for(int i = head[u] ; i ; i = E[i].next) {
	    int v = E[i].to;
	    if(v != fa) {
		++son;
		sum += dp[v];
		if(dp[v]) ++cnt;
	    }
	}
	++son;sum += fr[u];if(fr[u]) ++cnt;
	if(cnt < son - 1) sum += son - 1 - cnt;
	ans = min(ans,sum + 1);
    }
    sum = 0,son = 0,cnt = 0;
    if(fa != -1) {sum += fr[u];++son;if(fr[u]) ++cnt;}
    for(int i = head[u] ; i ; i = E[i].next) {
	int v = E[i].to;
	if(v != fa) {
	    ++son;
	    sum += dp[v];
	    if(dp[v]) ++cnt;
	}
    }
    for(int i = head[u] ; i ; i = E[i].next) {
	int v = E[i].to;
	if(v != fa) {
	    int t = dp[v] > 0;
	    fr[v] = sum - dp[v];
	    if(son - 2 > cnt - t) fr[v] += son - 2 - cnt + t;
	    dfs2(v,u);
	}
    }
}
void Solve() {
    int x,y;
    read(N);
    for(int i = 1 ; i < N ; ++i) {
	read(x);read(y);
	add(x,y);add(y,x);
    }
    dfs1(0,-1);
    ans = dp[0] + 1;
    dfs2(0,-1);
    out(ans);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
}

F - XOR Tree

如果你很熟练,你可以想到给一条路径加异或就相当于给两个端点到根加异或

我们从底到根算出每个点都需要加多少异或,然后给相同的异或值两两配对,这个时候会有单出来的

例如
1 2 3
我们可以2次解决而不是3次

我们设\(f[S]\)\(S\)集合中的点所需要最少的操作次数,\(S\)里的异或值为0时,初始值是\(S\)中1的个数-1,否则为\(S\)中1的个数

然后用子集枚举计算,复杂度是\(3^15\)

代码

#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('\n')
#define space putchar(' ')
#define MAXN 100005
#define eps 1e-8
//#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;
struct node {
    int to,next,val;
}E[MAXN * 2];
int head[MAXN],sumE;
int C[MAXN],cnt[25],f[(1 << 15) + 5],siz[MAXN];
void add(int u,int v,int c) {
    E[++sumE].to = v;
    E[sumE].next = head[u];
    E[sumE].val = c;
    head[u] = sumE;
}
void dfs(int u,int fa) {
    for(int i = head[u] ; i ; i = E[i].next) {
        int v = E[i].to;
        if(v != fa) {
            C[v] ^= E[i].val;
            dfs(v,u);
            siz[u] ^= siz[v];
        }
    }
    C[u] ^= siz[u];
    siz[u] ^= C[u];
}
void Solve() {
    read(N);
    int x,y,a;
    for(int i = 1 ; i < N ; ++i) {
        read(x);read(y);read(a);
        add(x,y,a);add(y,x,a);
    }
    dfs(0,-1);
    for(int i = 1 ; i < N ; ++i) {
        cnt[C[i]]++;
    }
    int ans = 0,q = 0;
    for(int i = 1 ; i <= 15 ; ++i) {
        ans += cnt[i] / 2;
        cnt[i] %= 2;
        if(cnt[i]) q |= 1 << (i - 1);
    }
    for(int i = 1 ; i < (1 << 15) ; ++i) {
        int a = 0,c = 0;
        for(int j = 1 ; j <= 15 ; ++j) {
            if(i >> (j - 1) & 1) {
                a ^= j;
                ++c;
            }
        }
        if(!a) f[i] = c - 1;
        else f[i] = c;
    }
    for(int S = 1 ; S < (1 << 15) ; ++S) {
        for(int T = (S - 1) & S ; T ; T = (T - 1) & S) {
            f[S] = min(f[S],f[T] + f[S ^ T]);
        }
    }
    ans += f[q];
    out(ans);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}

G - Colorful Doors

在很久以前的NOI集训……我曾经见过这题……然后成功爆0

现在我还是看不懂orz

分类讨论大题真是

我们把首尾的两个门连起来,相当于一个环

考虑全覆盖的情况,如果N是偶数
我们可以
1 2 1 2 3 4 3 4这么覆盖
如果N是奇数,由于1的时候有两个环之后N每+1环个数的奇偶性改变,我们总到不了一个环的时候,所以无解

然后我们如果有一个串010110111
我们在前面加上一个1

变成1010110111

我们统计一下两边都是1的门的个数,记为sum

如果sum 是奇数,显然不存在,因为两边都是1的门要两两配对

如果sum是4的倍数,我们把两端连续的1最后一个和第一个写成同样字母,可以变成全部覆盖且N为偶数的情况

如果sum是偶数
那么如果这些两边都是1的门是连到一起的,就相当于N是奇数的情况

如果两边都是1的门至少有两段分开的,我们就可以
1->2->3 4->5->6把2和5变成一种颜色,然后就变成了1->25->6->4->52->3这样的链

代码

#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('\n')
#define space putchar(' ')
#define eps 1e-8
#define MAXN 200005
#define mo 974711
//#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,ans[MAXN],tot,t[MAXN],to[MAXN];
char s[MAXN];
vector<int> f;
bool vis[MAXN];
void Solve() {
    read(N);
    scanf("%s",s + 2);
    s[1] = '1';
    int cnt = 0;
    for(int i = 1 ; i < 2 * N ; ++i) {
	    if(s[i] == '1' && s[i + 1] == '1') {++cnt;t[i] = 1;}
    }
    if(s[2 * N] == '1') {++cnt;t[2 * N] = 1;}
    if(cnt & 1) {puts("No");return;}
    bool flag = 0;
    if(cnt % 4 != 0) {
	int a = 1,b = 2 * N;
	int c = 0;
	while(t[a] == 1 && a <= 2 * N) ++a;
	--a;

	while(t[b] == 1 && b >= 1) --b;
    	++b;
    	if(a > b) {puts("No");return;}
    	if(a >= 1 || b <= 2 * N) ++c;
    	for(int i = a + 1 ; i <= b - 1 ; ++i) {
    	    if(t[i] == 1 && t[i - 1] != 1) ++c;
    	}
    	if(c < 2) {puts("No");return;}
        if(c == 2 && a < 1 && b <= 2 * N) flag = 1;
    }
    puts("Yes");
    if(flag) {
        f.clear();
        int ano;
        for(int i = 1 ; i <= 2 * N ; ++i) {
            if(t[i] == 1) {
                ans[i] = ++tot;ans[2 * N] = tot;
                ano = i;
                break;
            }
        }
        int p = 2 * N;
        while(t[p] == 1) --p;
        ans[p] = ++tot;ans[1] = tot;
        for(int i = p + 1 ; i < 2 * N ; ++i) f.pb(i);
        p = ano;
        while(t[p] == 1) ++p;
        for(int i = ano + 1 ; i < p ; ++i) f.pb(i);
        while(1) {
            int h = p;
            while(s[h + 1] == '0') ++h;
            if(ans[h]) break;
            ans[h] = ++tot;ans[p] = tot;
            p = h + 1;
        }
        ans[p] = ++tot;ans[ano - 1] = tot;
        int siz = f.size();
        for(int i = 0 ; i < siz ; i += 4) {
            ans[f[i]] = ++tot;ans[f[i + 2]] = tot;
            ans[f[i + 1]] = ++tot;ans[f[i + 3]] = tot;
        }
        f.clear();
        for(int i = 1 ; i <= 2 * N ; ++i) if(!ans[i]) f.pb(i);
        siz = f.size();
        for(int i = 0 ; i < siz ; i += 2) {
            ans[f[i]] = ++tot;ans[f[i + 1]] = tot;
        }
        for(int i = 1 ; i <= 2 * N ; ++i) {out(ans[i]);space;}
        enter;
        return ;
    }
    if(cnt % 4 == 2) {

    	for(int i = 1 ; i <= 2 * N ; ++i) {
    	    if(t[i] == 1 && t[i - 1] == 0) f.pb(i);
    	}
    	ans[f[0]] = ++tot;ans[f[1]] = tot;
        to[f[0]] = f[1] + 1;to[f[1]] = f[0] + 1;
    	int k = f[1];
    	while(t[k] == 1) ++k;
    	ans[k] = ++tot;ans[f[1] - 1] = tot;
    	to[k] = f[1] - 1;to[f[1] - 1] = k;
    }

    int p = 1;
    f.clear();
    while(p <= 2 * N) {
    	vis[p] = 1;
    	if(t[p] && !ans[p]) f.pb(p);
    	if(p >= 2 * N) break;
    	if(to[p] && !vis[to[p]]) p = to[p];
    	else if(s[p + 1] == '1' && !vis[p + 1]) ++p;
    	else {
    	    int k = p;
    	    while(k < 2 * N && (s[k + 1] == '0' || vis[k])) ++k;
    	    ans[p] = ++tot;ans[k] = tot;
    	    p = k + 1;
    	    while(p <= 2 * N && vis[p]) ++p;
    	}

    }
    int siz = f.size();
    for(int i = 0 ; i < siz ; i += 4) {
        ans[f[i]] = ++tot;ans[f[i + 2]] = tot;
        ans[f[i + 1]] = ++tot;ans[f[i + 3]] = tot;
    }
    f.clear();
    for(int i = 1 ; i <= 2 * N ; ++i) {
    	if(!ans[i]) f.pb(i);
    }
    siz = f.size();
    for(int i = 0 ; i < siz ; i += 2) {
        ans[f[i]] = ++tot;ans[f[i + 1]] = tot;
    }
    for(int i = 1 ; i <= 2 * N ; ++i) {out(ans[i]);space;}
    enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
}

H - Generalized Insertion Sort

为了调这题我特意写了spj,然后发现我数组开小了

简直zz

刷atcoder真是锻炼了我写spj的能力QAQ

我们考虑把所有从叶子开始的一条链挑出来,每次处理它们并删掉,这样只有logn层,因为最多的情况就是一个满二叉树

然后我们如果有一个红点来到根,它所在的底部链有一个红点序列,我们类似插入排序把它插入该到的地方

如果有一个白点,我们把它扔到深度最大的地方,且没有排序过的红点,并标称黑点

但是黑点有可能被顶上去,我们发现黑点被顶上去只可能是放了一个红点,所以复杂度均摊下来就是\(n \log n + n\)

#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('\n')
#define space putchar(' ')
#define eps 1e-8
#define MAXN 4005
#define mo 974711
//#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,fa[MAXN],son[MAXN];
vector<int> ne[MAXN];
int a[MAXN],pos[MAXN];
int que[MAXN],tot,pre[MAXN],red,bot[MAXN];
bool vis[MAXN],cov[MAXN],bl[MAXN];
int ans[25005],q;
void drag(int v) {
    if(v == 0) return;
    ans[++q] = v;
    int u = v;
    int t = a[u];
    while(fa[u] != -1) {
	int k = a[fa[u]];
	a[fa[u]] = t;
	pos[t] = fa[u];
	t = k;
	u = fa[u];
    }
    a[v] = t;pos[t] = v;
}
void Process() {
    while(red) {
	if(vis[a[0]]) {
	    int u = bot[a[0]];
	    //while(u != -1 && u == a[u]) {cov[u] = 1;u = fa[u];}
	    while(1) {
		if(!cov[u] || a[u] < a[0]) break;
		u = fa[u];
	    }
	    drag(u);
	    while(cov[u]) u = fa[u];
	    cov[u] = 1;
	    --red;
	}
	else {
	    for(int i = N - 1 ; i >= 0 ; --i) {
		if(!cov[i] && !bl[a[i]]) {
		    bl[a[0]] = 1;
		    drag(i);break;
		}
	    }
	}
    }
}
void Solve() {
    read(N);
    fa[0] = -1;
    for(int i = 1 ; i < N ; ++i) {
	read(fa[i]);son[fa[i]]++;
	ne[fa[i]].pb(i);
    }
    for(int i = 0 ; i < N ; ++i) {
	read(a[i]);
	pos[a[i]] = i;
    }
    while(1) {
	tot = 0;
	memset(pre,0,sizeof(pre));
	memset(bl,0,sizeof(bl));
	memset(bot,0,sizeof(bot));
	red = 0;
	for(int i = N - 1 ; i >= 0 ; --i) {
	    if(!vis[i]) {
		if(!son[i] || (son[i] == 1 && pre[i])) {
		    que[++tot] = i;
		    if(fa[i] != -1 && son[fa[i]] == 1) pre[fa[i]] = i;
		    if(pre[i]) bot[i] = bot[pre[i]];
		    else bot[i] = i;
		    vis[i] = 1;
		    ++red;
		}
	    }
	}
	if(!red) break;
	Process();
	for(int i = 1 ; i <= tot ; ++i) {
	    --son[fa[que[i]]];
	}
    }
    out(q);enter;
    for(int i = 1 ; i <= q ; ++i) {
	out(ans[i]);enter;
    }
}

int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
}

I - Simple APSP Problem

一开始想到离散化之后各种分类讨论,瞬间不可写了QAQ

最后就了解了一下简便的写法

就是对于两个相邻的空行,我们把跨过它的贡献统计出来,把它缩成一行,列也一样,这样一个点内之间的路径长度都是0

这样就变成了\(2n * 2n\)的一个矩形,每个矩形里面有一个值代表这个点原来方块的大小

然后对于每个点用BFS跑最短路,两两枚举点对加上即可

代码

#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('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 1000005
//#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;

int H,W,N;
int x[35],y[35];
int sum[MAXN],a[MAXN],nxtr[MAXN],nxtc[MAXN];
int pos[MAXN],ans;
int g[65][65],r,c,f[65][65];
bool vis[65][65];
int dx[] = {0,-1,0,1},dy[] = {1,0,-1,0};

int mul(int a,int b) {
    return 1LL * a * b % MOD;
}
int inc(int a,int b) {
    return a + b >= MOD ? a + b - MOD : a + b;
}
void update(int &x,int y) {
    x = inc(x,y);
}
queue<pii > Q;
void BFS(int x,int y) {
    memset(vis,0,sizeof(vis));
    vis[x][y] = 1;f[x][y] = 0;
    Q.push(mp(x,y));
    while(!Q.empty()) {
	pii t = Q.front();Q.pop();
	for(int k = 0 ; k < 4 ; ++k) {
	    int mx = t.fi + dx[k],my = t.se + dy[k];
	    if(mx >= 1 && mx <= r && my >= 1 && my <= c) {
		if(!vis[mx][my] && g[mx][my] != -1) {
		    f[mx][my] = f[t.fi][t.se] + 1;
		    Q.push(mp(mx,my));
		    vis[mx][my] = 1;
		}
	    }
	}
    }
}
void Solve() {
    read(H);read(W);
    read(N);
    for(int i = 1 ; i <= N ; ++i) {
	read(x[i]);read(y[i]);
	++x[i];++y[i];
    }
    for(int i = 1 ; i <= H ; ++i) a[i] = W;
    a[H + 1] = 0;
    for(int i = 1 ; i <= N ; ++i) --a[x[i]];
    for(int i = 1 ; i <= H ; ++i) {
	sum[i] = inc(sum[i - 1],a[i]);
    }
    memset(pos,0,sizeof(pos));
    for(int i = 1 ; i <= H ; ++i) pos[i] = i;
    for(int i = 1 ; i <= H ; ++i) {
	if(a[i] == W && a[i + 1] == W) {
	    update(ans,mul(sum[i],inc(sum[H],MOD - sum[i])));
	    pos[i + 1] = pos[i];
	    nxtr[pos[i]] = i + 2;
	}
	else nxtr[pos[i]] = i + 1;
    }
    for(int i = 1 ; i <= W ; ++i) a[i] = H;
    a[W + 1] = 0;
    for(int i = 1 ; i <= N ; ++i) a[y[i]]--;
    for(int i = 1 ; i <= W ; ++i) sum[i] = inc(sum[i - 1],a[i]);
    memset(pos,0,sizeof(pos));
    for(int i = 1 ; i <= W ; ++i) pos[i] = i;
    for(int i = 1 ; i <= W ; ++i) {
	if(a[i] == H && a[i + 1] == H) {
	    update(ans,mul(sum[i],inc(sum[W],MOD - sum[i])));
	    pos[i + 1] = pos[i];
	    nxtc[pos[i]] = i + 2;
	}
	else nxtc[pos[i]] = i + 1;
    }
    int tmp = 1;
    while(tmp != H + 1) {tmp = nxtr[tmp];++r;}
    tmp = 1;
    while(tmp != W + 1) {tmp = nxtc[tmp];++c;}
    int p1 = 1;
    for(int i = 1 ; i <= r ; ++i) {
	int p2 = 1;
	for(int j = 1 ; j <= c ; ++j) {
	    g[i][j] = mul(nxtr[p1] - p1,nxtc[p2] - p2);
	    if(nxtr[p1] - p1 == 1 && nxtc[p2] - p2 == 1) {
		for(int k = 1 ; k <= N ; ++k) {
		    if(x[k] == p1 && y[k] == p2) {
			g[i][j] = -1;
			break;
		    }
		}
	    }
	    p2 = nxtc[p2];
	}
	p1 = nxtr[p1];
    }
    tmp = 0;
    for(int i = 1 ; i <= r ; ++i) {
	for(int j = 1 ; j <= c ; ++j) {
	    if(g[i][j] != -1) {
		BFS(i,j);
		for(int k = 1 ; k <= r ; ++k) {
		    for(int h = 1 ; h <= c ; ++h) {
			if(g[k][h] != -1) update(tmp,mul(f[k][h],mul(g[i][j],g[k][h])));
		    }
		}
	    }
	}
    }
    tmp = mul(tmp,(MOD + 1) / 2);
    update(ans,tmp);
    out(ans);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
}

J - Rectangles

神仙数数题呀QAQ

计算出至少有一维是对齐的方案数,可以用容斥
至少一维对齐-至少两维对齐 + 至少三维对齐

然后计算每一维都不对齐的方案数

对于\(p + i,r + j,q + k\)这个点标上\(k\)
那么对于前两维,我对于\(v(x,y,z)\)从下往上一定可以得到一个0,1,2,3,4...c-1,0,1,2,3,4..c - 1的的循环同构串

那么我对于最底层的平面,显然这是一个填满了数的矩形,我们要把它划分成\(a*b\)的矩形,使得每个矩形里数字都一样,问方案数

如果有一行或一列错开了,且这行这列填的数互不相同,那这种情况一定是某一维对齐的情况

所以我们的要填的矩形应该是一个正好被划分成了\(\frac{A*B}{a*b}\)个小矩形
这个时候有一个高度\(h\),它至少占有了一行一列,然后至少在某一层,它移动了某一行,至少在另一层,它动了某一列
且这个高度\(h\)只有一个

我们记占有的行数为\(p\),列数为\(q\),方案数是\((a^q + b^p - 1)^{C / c} - (a^q)^{C / c} - (b^p)^{C / c} + 1\)

然后剩下的要求这种颜色填不成新的行和列,用容斥一下就行,至少零行一样 - 至少一行一样+ 至少两行一样-至少三行一样
列不一一样可以用\(c^{i} - 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('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 1000005
//#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;
int A,B,C,a,b,c;
int ans;
int binom[105][105],f[105][105],g[105][105];
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;
}
int Calc(int a,int A,int b,int B) {
    int res = 0;
    update(res,mul(b,fpow(a,B / b)));
    update(res,mul(a,fpow(b,A / a)));
    update(res,MOD - mul(a,b));
    return res;
}
void Trivial() {
    update(ans,mul(c,fpow(Calc(a,A,b,B),C / c)));
    update(ans,mul(b,fpow(Calc(a,A,c,C),B / b)));
    update(ans,mul(a,fpow(Calc(b,B,c,C),A / a)));
    update(ans,MOD - mul(mul(b,c),fpow(a,B / b * C / c)));
    update(ans,MOD - mul(mul(a,c),fpow(b,A / a * C / c)));
    update(ans,MOD - mul(mul(a,b),fpow(c,A / a * B / b)));
    update(ans,mul(mul(a,b),c));
    
}
void Solve() {
    Trivial();
    binom[0][0] = 1;
    for(int i = 1 ; i <= 100 ; ++i) {
	binom[i][0] = 1;
	for(int j = 1 ; j <= i ; ++j) {
	    binom[i][j] = inc(binom[i - 1][j - 1],binom[i - 1][j]);
	}
    }
    for(int i = 0 ; i <= A / a ; ++i) {
	for(int j = 0 ; j <= B / b ; ++j) {
	    int t = 1;
	    for(int k = 0 ; k <= i ; ++k) {
		int tmp = inc(fpow(c,i - k),MOD - 1);
		tmp = fpow(tmp,j);
		update(g[i][j],mul(mul(tmp,binom[i][k]),t));
		t = mul(t,MOD - 1);
	    }
	}
    }
    int all = 0;
    for(int i = 1 ; i <= A / a ; ++i) {
	for(int j = 1 ; j <= B / b ; ++j) {
	    if(i == A / a && j == B / b) continue;
	    int k = fpow(a,j),t = fpow(b,i);
	    update(f[i][j],fpow(inc(inc(k,t),MOD - 1),C / c));
	    update(f[i][j],MOD - fpow(k,C / c));
	    update(f[i][j],MOD - fpow(t,C / c));
	    update(f[i][j],1);
	    int tmp = mul(f[i][j],mul(binom[A / a][i],binom[B / b][j]));
	    tmp = mul(tmp,c);
	    tmp = mul(tmp,g[A / a - i][B / b - j]);
	    update(all,tmp);
	}
    }
    all = mul(all,mul(a,b));
    update(ans,all);
    out(ans);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    read(a);read(b);read(c);read(A);read(B);read(C);
    if(A % a == 0 && B % b == 0 && C % c == 0) Solve();
    else puts("0");
    return 0;
}
posted @ 2018-12-05 15:26  sigongzi  阅读(398)  评论(0编辑  收藏  举报