Southern and Volga Russia Qualifier 2019-2020 gym102348

文章目录

  • A-Yellow Cards(思维)
  • B-Interesting Vertices(dfs回溯)
  • C-Marbles(状压dp)
  • D-Ticket Game(思维博弈)
  • E-Painting The Fence(贪心+优先队列)
  • F-The Number of Products(暴力)
  • G-Swap Letters(思维)
  • H-Berland Prospect(线性dp)
  • I-Radio Stations(2-sat+技巧建图)
  • J-Monocarp and T-Shirts(期望+容斥)
  • K-Moonbound(模拟)
  • L-Printer(暴力)

A-Yellow Cards(思维)

题目大意:给你两个队伍,人数分别为 a 1 、 a 2 a1、a2 a1a2,每个队每个人最多收到 k 1 、 k 2 k1、k2 k1k2张黄牌后被罚下场,一共有 n n n张黄牌,求最少和最多有多少人被罚下场。

解题思路:贪心求,每个人同等考虑,最少的肯定是每个人都至少收到 k − 1 k-1 k1张牌后离场,最多的是优先下场 k k k值小的队员。

AC_code:

#include
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 998244353;
const int inf = 1e9+7;
const LL INF = 1e18+7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;

int a1, a2, k1, k2, n;

int main() {
#ifndef ONLINE_JUDGE
    //freopen("out.txt", "w", stdout);
    //freopen("in.txt", "r", stdin);
#endif
    while(~scanf("%d%d%d%d%d", &a1, &a2, &k1, &k2, &n)) {
        int ans1 = max(0, n-a1*(k1-1)-a2*(k2-1)), ans2 = 0;
        if(k1 < k2) ans2 += min(a1, n/k1), n -= ans2*k1, ans2 += min(a2, n/k2);
        else ans2 += min(a2, n/k2), n -= ans2*k2, ans2 += min(a1, n/k1);
        printf("%d %d\n", ans1, ans2);
    }
#ifndef ONLINE_JUDGE
    //cout <<"It costs " <
#endif
    return 0;
}

B-Interesting Vertices(dfs回溯)

题目大意:给你一课有 n n n个节点的树,其中有 k k k个节点被染色,求有多少个节点满足自身没有被染色并且它的每棵子树中都至少有一个节点被染色。

解题思路: d f s dfs dfs回溯类似求树的重心的方式求解, d f s dfs dfs回溯可以得到每个节点的它的子树中是否都有染色的点并统计所有子树中染色点的个数 s u m sum sum,然后走向父亲的那棵子树中被染色的点数就为 k − s u m k-sum ksum,这样即可判断此节点是否满足条件。

AC_code:

#include
 
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
 
int n, k, cnt;
int c[200010], head[200010], vis[200010], sz[200010];
 
struct edge {
    int to, nxt;
}e[200010*2];
 
void add(int u, int v) {
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt ++;
}
 
vector<int> ans;
 
void dfs(int u) {
    vis[u] = 1;
    sz[u] = c[u];
    int flag = 1;
    for(int i = head[u]; ~i; i = e[i].nxt) {
        int en = e[i].to;
        if(vis[en]) continue;
        dfs(en);
        if(!sz[en]) flag = 0;
        sz[u] += sz[en];
    }
    if(!c[u] && flag && (k > sz[u] || u == 1)) ans.push_back(u);
}
 
int main() {
    int x, y;
    while(~scanf("%d%d", &n, &k)) {
        mem0(vis);mem0(c);mem1(head);
        cnt = 0;
        ans.clear();
        for(int i = 0; i < k; i ++) {
            scanf("%d", &x);
            c[x] = 1;
        }
        for(int i = 0; i < n-1; i ++) {
            scanf("%d%d", &x, &y);
            add(x, y), add(y, x);
        }
        dfs(1);
        sort(ans.begin(), ans.end());
        printf("%d\n", ans.size());
        for(auto i : ans) {
            printf("%d ", i);
        }
        printf("\n");
    }
    return 0;
}

C-Marbles(状压dp)

题目大意:给你一个数组,每次你可以选择相邻的两个元素进行交换,让你求最少的交换次数使得这和数组所有相同的元素都在一块。

解题思路:因为数组的元素最多只有 20 20 20个,所以我们可以用 20 20 20位的状压 d p dp dp来解决这个问题, d p [ i ] dp[i] dp[i]表示 i i i状态表示的数字都已经在一块,排在前面的最少交换次数,那么转移就为 d p [ i ] = m i n ( d p [ i ] , d p [ i ( 1 < < j ) + c o s t ) dp[i] = min(dp[i], dp[i^(1<dp[i]=min(dp[i],dp[i(1<<j)+cost),这里的 c o s t cost cost就是再把除了前面排好的 i i i^ ( 1 < < j ) (1<(1<<j)种数字其他的数字都移动到第 j j j种数字后面的花费。计算这种花费的话,我们可以预处理出每种字符移动到最前面的花费,统计出有多少的其他字符在他前面就是的了,减去不需要移动的哪几种就可以得到 c o s t cost cost了。

AC_code:

#include
 
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
 
int n;
int a[400010];
LL dp[1<<20], cost[25][25], pre[25][400010], s[25];
int id[25];
int main() {
    while(~scanf("%d", &n)) {
        mem1(id);
        mem0(cost);
        mem0(pre);
        int cnt = 0;
        for(int i = 1; i <= n; i ++) {
            scanf("%d", &a[i]);
            if(id[a[i]] == -1) id[a[i]] = cnt ++;
        }
        for(int i = 0; i < cnt; i ++) {
            int now = 1;
            s[i] = 0;
            for(int j = 1; j <= n; j ++) {
                if(id[a[j]] == i) s[i] += abs(j-now ++);
            }
            //printf("%d %lld\n", i, s[i]);
        }
        for(int i = 0; i < cnt; i ++) {
            pre[i][0] = 0;
            for(int j = 1; j <= n; j ++) {
                pre[i][j] = pre[i][j-1] + (id[a[j]] == i);
            }
        }
        for(int i = 1; i <= n; i ++) {
            for(int j = 0; j < cnt; j ++) {
                if(id[a[i]] == j) continue;
                cost[id[a[i]]][j] += pre[j][n] - pre[j][i];
            }
        }
        for(int i = 0; i < cnt; i ++) {
            for(int j = 0; j < cnt; j ++) {
                //printf("cost[%d][%d] = %lld\n", i, j, cost[i][j]);
            }
        }
        dp[0] = 0;
        for(int i = 1; i < 1<<cnt; i ++) {
            dp[i] = INF;
            int sz = 0;
            for(int j = 0; j < cnt; j ++) {
                if(!(i&(1<<j))) continue;
                LL val = 0;
                for(int k = 0; k < cnt; k ++) {
                    if(!(i&(1<<k)) || k == j) continue;
                    val += cost[k][j];
                }
                dp[i] = min(dp[i], dp[i^(1<<j)] + s[j] - val);
            }
        }
        printf("%lld\n", dp[(1<<cnt)-1]);
    }
    return 0;
}

D-Ticket Game(思维博弈)

题目大意:给你一个字符串,包含字符’0’ ~ ‘9‘以及 ‘ ? ’ ‘?’ ?,每个人可以对 ′ ? ′ '?' ?先后进行填数0~9,Monocarp先手,如果最后字符串前半部分的数字和等于后半部分则Bicarp胜。

解题思路:1、首先对于’?'只在前半部分,或者只在右半部分的情况,发现无论Monocarp怎么填,Bicarp都能使得两个人各填一个问号后,使得问号这半增加 9 9 9,所以推出只有在两边只差为 9 ∗ 问 号 数 除 以 二 9*问号数除以二 9的时候Bicarp必胜。2、当两半都有问号时,此时两人的优势都是一样的,所以只能维持原状态(你想使某一半增加多少,我就使另一半也增加呗,不让你得到想要的状态),所以最后一定又会回到1的那种情况。

AC_code:

#include
 
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
 
int n;
char s[200010];
 
int main() {
    while(~scanf("%d", &n)) {
        scanf("%s", s);
        int s1 = 0, s2 = 0, a = 0, b = 0;
        for(int i = 0; i < n/2; i ++) {
            if(s[i] == '?') s1 ++;
            else a += s[i]-'0';
        }
        for(int i = n/2; i < n; i ++) {
            if(s[i] == '?') s2 ++;
            else b += s[i]-'0';
        }
        if(a-b == (s2-s1)/2*9) printf("Bicarp\n");
        else printf("Monocarp\n");
    }
    return 0;
}

E-Painting The Fence(贪心+优先队列)

题目大意:给你 m m m种数字一共 n n n个,让你从前往后填,相同的数字最多连续 k k k个,让你输出构造的方案,构造不了输出 ‘ − 1 ’ ‘-1’ 1

解题思路:贪心的思路,优先选择数量多的先填,这样会让最后剩余相同的数字数量最少,所以我们优先选数量最多的两种数字填,最后剩下的(某一种)就填到它前面的位置去,一定是和相同的填在一起,这里就不证明了,自己画下就可以得到。优先队列模拟即可。

AC_code:

#include
 
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
 
int n, m, k;
int a[200010], s[200010];
 
struct node {
    int x, s;
    bool operator<(const node &a) const {
        return s < a.s;
    }
};
 
priority_queue<node> q;
 
int main() {
    int x;
    while(~scanf("%d%d%d", &n, &m, &k)) {
        mem0(s);
        mem0(a);
        while(!q.empty()) q.pop();
        for(int i = 1; i <= m; i ++) {
            node cur;
            scanf("%d", &cur.s);
            cur.x = i;
            q.push(cur);
        }
        int cnt = 0;
        while(!q.empty()) {
            if(q.size() >= 2) {
                node n1 = q.top();q.pop();
                node n2 = q.top();q.pop();
                a[cnt ++] = n1.x;
                a[cnt ++] = n2.x;
                n1.s --;
                n2.s --;
                if(n1.s) q.push(n1);
                if(n2.s) q.push(n2);
            }else {
                if(a[cnt-1] != q.top().x) {
                    a[cnt ++] = q.top().x;
                    node cur = q.top();q.pop();
                    cur.s --;
                    if(cur.s) q.push(cur);
                }
                break;
            }
        }
        for(int i = 0; i < cnt && !q.empty(); i ++) {
            if(a[i] == q.top().x) {
                s[i] += min(q.top().s, k-1);
                node cur = q.top();q.pop();
                cur.s -= min(cur.s, k-1);
                if(cur.s > 0) q.push(cur);
                else break;
            }
        }
        if(!q.empty()) printf("-1");
        else
        for(int i = 0; i < cnt; i ++) {
            for(int j = 0; j <= s[i]; j ++) {
                printf("%d ", a[i]);
            }
        }
        printf("\n");
    }
    return 0;
}

F-The Number of Products(暴力)

题目大意:给你一个数组,让你求区间乘积为负、零、正的个数。

解题思路:直接暴力找即可,找零的时候注意下去重。

AC_code:

#include
 
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
 
int n;
int bit[200010];
int a[200010];
 
int main() {
    while(~scanf("%d", &n)) {
        LL ans1 = 0, ans2 = 0, ans3 = 0;
        int pre1 = 1, pre2 = 0, pre0 = 0, now = 1;
        for(int i = 1; i <= n; i ++) {
            scanf("%d", &a[i]);
            if(a[i] == 0) {
                pre1 = 1, pre2 = 0;
                now = 1;
                ans3 += 1LL*(i-pre0)*(n-i+1);
                pre0 = i;
                continue;
            }
            if(a[i] > 0) now *= 1;
            if(a[i] < 0) now *= -1;
            if(now > 0) ans1 += pre1, ans2 += pre2, pre1 ++;
            else ans1 += pre2, ans2 += pre1, pre2 ++;
        }
        printf("%lld %lld %lld\n", ans2, ans3, ans1);
    }
    return 0;
}

G-Swap Letters(思维)

题目大意:给你两个字符串,只有 ‘ a ’ ′ b ′ ‘a’'b' ab两种字符,每次操作你可以分别选择两个字符串中的两个字符进行交换,问最少几次操作可以使得两个字符串相同,无解输出 ‘ − 1 ’ ‘-1’ 1,注意不能交换同一个字符串中的字符。

解题思路:贪心求解,位置相同且字符相同的不交换,那么最后只剩下 a b ab ab b a ba ba这种字符对,两对相同的 a b ab ab b a ba ba通过一次交换可以使得这两对相同,一对 a b ab ab b a ba ba则需要交换两次,直接统计答案即可。

AC_code:

#include
 
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
 
int n;
char s[200010], t[200010];
vector<int> v1, v2;
 
int main() {
    while(~scanf("%d", &n)) {
        scanf("%s%s", s, t);
        v1.clear(), v2.clear();
        for(int i = 0; i < n; i ++) {
            if(s[i] == t[i]) continue;
            if(s[i] == 'a') v1.push_back(i+1);
            else v2.push_back(i+1);
        }
        if(v1.size()%2 != v2.size()%2) printf("-1\n");
        else {
            int sum = v1.size()/2 + v2.size()/2 + v1.size()%2*2;
            printf("%d\n", sum);
            while(v1.size() >= 2) {
                printf("%d %d\n", v1[v1.size()-1], v1[v1.size()-2]);
                v1.pop_back();
                v1.pop_back();
            }
            while(v2.size() >= 2) {
                printf("%d %d\n", v2[v2.size()-1], v2[v2.size()-2]);
                v2.pop_back();
                v2.pop_back();
            }
            if(v1.size()) {
                printf("%d %d\n", v1[0], v1[0]);
                printf("%d %d\n", v1[0], v2[0]);
            }
        }
    }
    return 0;
}

H-Berland Prospect(线性dp)

题目大意:给你一个递增的数组,让你找一个最长的等差序列,输出长度。

解题思路: d p [ i ] [ j ] dp[i][j] dp[i][j]表示从位置 i i i开始公差为 k = a [ j ] − a [ i ] k = a[j] - a[i] k=a[j]a[i]的最长序列,那么我们从后往前就有 d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , m a x ( 2 , d p [ j ] [ i d x [ a [ j ] + k ] ] + 1 ) ) dp[i][j] = max(dp[i][j], max(2, dp[j][idx[a[j]+k]]+1)) dp[i][j]=max(dp[i][j],max(2,dp[j][idx[a[j]+k]]+1)),记录最大值即可。注意:此题卡 m a p Q A Q mapQAQ mapQAQ m a p map map离散化会 T T T,需要二分查找下标。

AC_code:

#include
 
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
 
int n;
LL x[3010];
int dp[3010][3010];
 
int main() {
    while(~scanf("%d", &n)) {
        mem0(dp);
        int cnt = 0;
        for(int i = 0; i < n; i ++) {
            scanf("%lld", &x[i]);
        }
        int ans = 2;
        for(int i = n-1; i >= 0; i --) {
            for(int j = i+1; j < n; j ++) {
                LL k = x[j]-x[i];
                int id = lower_bound(x, x+n, x[j]+k)-x;
                if(x[id] != x[j]+k) {
                    dp[i][j] = 2;continue;
                }
                dp[i][j] = max(dp[i][j], max(2, dp[j][id]+1));
                ans = max(ans, dp[i][j]);
                //printf("dp[%d][%d] = %d dp[%d][%d] = %d\n", i, j, dp[i][j], j, id, dp[j][id]);
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

I-Radio Stations(2-sat+技巧建图)

题目大意:有 n n n个城市要布置电台,第 i i i个城市至少要安装 x i xi xi y i yi yi中的一个,接下来给你 p p p个电台,电台 i i i的功率范围为 l i − r i li-ri liri,最后还有 m m m个限制条件,第 i i i个限制条件为电台 u i ui ui v i vi vi不能都被安装,最后让你找出一种电台的安装方案使得满足这 n + m n+m n+m个条件,并且至少有一个功率值 f f f满足你选出来的这 k k k个电台都满足 l i < = f < = r i li<=f<=ri li<=f<=ri,输出 k k k f f f以及选了哪k个电台。

解题思路:假如没有功率范围的限制,这题就是个很裸的 2 − s a t 2-sat 2sat问题,现在有了功率范围的限制,我们需要把这些限制条件也转化为 2 − s a t 2-sat 2sat的模型:这里的 n + m n+m n+m个条件的连边方式我就不讲了,主要讲下功率范围的连边方式,重新构造 W ∗ 2 W*2 W2个点,点 i i i表示选择了条件 f > = i f >= i f>=i,反之则点 i + W i+W i+W表示选择了 f < i f < i f<i,这样由于大小关系,你选择了点 i i i,点 i − 1 i-1 i1就必选,直到点 1 1 1,同理,你选择了点 i + W i+W i+W,点 i + W + 1 i+W+1 i+W+1也必选,直到点 2 ∗ W 2*W 2W,所以我们如果选择了电台 x x x,那么我们必选点 l i li li r i + W + 1 ri+W+1 ri+W+1,反之如果选择了, l i + W li+W li+W r i + 1 ri+1 ri+1那么必不选 x x x,最后跑 2 − s a t 2-sat 2sat即可求解。

AC_code:

#include
 
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 998244353;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
const int MX = 2e6+5;
 
int n, p, M, m;
int cnt, idx, scc, k, out;
int head[MX], dfn[MX], low[MX], instack[MX], belong[MX];
int pos[MX], du[MX], output[MX];
stack<int> st;
vector<int> v[MX];
 
struct edge {
    int to;
    int nxt;
}e[MX*6];
 
void add(int u, int v) {
    //printf("------%d %d\n", u, v);
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt ++;
}
 
void tarjan(int k) {
    dfn[k] = low[k] = idx ++; ///idx初始化为1
    instack[k] = 1;
    st.push(k);
    for(int i = head[k]; i != -1; i = e[i].nxt) {
        int en = e[i].to;
        if(!dfn[en]) {
            tarjan(en);
            low[k] = min(low[en], low[k]);
        } else if(instack[en]) {
            low[k] = min(low[k], dfn[en]);
        }
    }
    if(dfn[k] == low[k]) {
        scc ++; ///缩点后的编号(缩点后强连通分量的个数)
        int v;
        do {
            v = st.top();
            st.pop();
            instack[v] = 0;
            belong[v] = scc;
        } while(k != v);
    }
}
 
int pt[MX];
 
void topo() {
    mem0(pt);
    queue<int> q;
    for(int i = 1; i <= scc; i ++) {
        if(du[i] == 0) q.push(i);
    }
    while(!q.empty()) {
        int u = q.front();q.pop();
        if(pt[u] == 0) {
            pt[u] = 1;
            pt[pos[u]] = 2;
        }
        for(int i = 0; i < v[u].size(); i ++) {
            int en = v[u][i];
            du[en] --;
            if(du[en] == 0) q.push(en);
        }
    }
    out = 0;
    for(int i = 1; i <= p; i ++) {
        if(pt[belong[i]] == 1) output[out ++] = i;
    }
}
 
bool solve() {
    while(!st.empty()) st.pop();
    for(int i = 1; i <= 2*(p+M); i ++) {
        v[i].clear();
        if(!dfn[i]) tarjan(i);
    }
    //printf("-----------%d\n", scc);
    for(int i = 1; i <= p; i ++) {
        if(belong[i] == belong[i+p]) return 0;
        pos[belong[i]] = belong[i+p];
        pos[belong[i+p]] = belong[i];
    }
    for(int i = p*2+1; i <= p*2+M; i ++) {
        if(belong[i] == belong[i+M]) return 0;
        pos[belong[i]] = belong[i+M];
        pos[belong[i+M]] = belong[i];
    }
    for(int i = 1; i <= p*2+M*2; i ++) {
        for(int j = head[i]; ~j; j = e[j].nxt) {
            int en = e[j].to;
            if(belong[i] != belong[en]) {
                du[belong[i]] ++;
                v[belong[en]].pb(belong[i]);
            }
        }
    }
    topo();
    return 1;
}
 
void init() {
    mem1(head);
    cnt = scc = 0;
    idx = 1;
    mem0(dfn); mem0(low); mem0(instack); mem0(belong);
    mem0(pos); mem0(du); mem0(output);
}
 
int l[400010], r[400010];
 
int main() {
    int x, y;
    while(~scanf("%d%d%d%d", &n, &p, &M, &m)) {
        init();
        for(int i = 0; i < n; i ++) {
            scanf("%d%d", &x, &y);
            add(x+p, y), add(y+p, x);
        }
        int id = p*2;
        for(int i = 1; i <= M; i ++) {
            if(i > 1) add(id+i, id+i-1);
            if(i < M) add(id+i+M, id+i+M+1);
        }
        for(int i = 1; i <= p; i ++) {
            scanf("%d%d", &x, &y);
            l[i] = x, r[i] = y;
            add(i, id+x);
            add(id+x+M, i+p);
            add(i, id+y+M+1);
            add(id+y+1, i+p);
        }
        for(int i = 0; i < m; i ++) {
            scanf("%d%d", &x, &y);
            add(x, y+p), add(y, x+p);
        }
        if(solve()) {
            int L = 0;
            for(int i = 0; i < out; i ++) {
                L = max(L, l[output[i]]);
            }
            printf("%d %d\n", out, L);
            for(int i = 0; i < out; i ++) {
                printf("%d ", output[i]);
            }
            printf("\n");
        }else printf("-1\n");
    }
    return 0;
}

J-Monocarp and T-Shirts(期望+容斥)

题目大意:众所周知,打ACM会发衣服,你有n场比赛,每场比赛你有P的概率得到一件大小为x-1的衣服,Q的概率得到大小为x+1的衣服,所以你有1-P-Q的概率得到大小为x的衣服,同时你有n个朋友想得到n件大小互不相同的衣服,问你能满足的朋友个数的期望。

解题思路:满足朋友的个数期望=每个朋友被满足的概率之和,所以我们求出每个朋友被满足的概率即可,计算概率时容斥一下即可(你有 0.8 0.8 0.8的概率满足 x x x,同时还有 0.7 0.7 0.7的概率也满足 x x x,两个概率为不同的事件,所以 p ( x ) = 0.8 + 0.7 − 0.8 ∗ 0.7 p(x) = 0.8+0.7-0.8*0.7 p(x)=0.8+0.70.80.7)

AC_code:

#include
 
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 998244353;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
 
int n;
LL p, q;
int a [200010];
 
LL ksm(LL a, int b) {
    LL ans = 1;
    while(b) {
        if(b&1) ans = ans*a%mod;
        a = a*a%mod;
        b /= 2;
    }
    return ans;
}
 
int main() {
    while(~scanf("%d%lld%lld", &n, &p, &q)) {
        for(int i = 0; i < n; i ++) {
            scanf("%d", &a[i]);
        }
        sort(a, a+n);
        LL r = (1000000LL-p-q)*ksm(1000000, mod-2)%mod;
        p = p*ksm(1000000, mod-2)%mod;
        q = q*ksm(1000000, mod-2)%mod;
        LL sum = 0;
        for(int i = 0; i < n; i ++) {
            int left = 0, right = 0;
            if(i > 0 && a[i-1]+1 == a[i]) left = 1;
            if(i < n-1 && a[i+1]-1 == a[i]) right = 1;
            sum += r;
            if(left) sum += q - r*q;
            if(right) sum += p - r*p;
            if(left && right) sum += r*p%mod*q%mod - p*q;
            sum = (sum%mod+mod)%mod;
        }
        printf("%lld\n", sum);
    }
    return 0;
}

K-Moonbound(模拟)

题目大意:给你一个nn的矩阵让你填,最后使得第i行j列为(i+j)%2 == 0 stand : stone,你可以选择一个一个11的填,也可以选择2*2的填,某个位置能填必须满足它在边界,后者有一个相邻的已经被填。

解题思路:模拟即可

AC_code:

#include
const int inf = 1e9+7;
using namespace std;
 
int n;
 
int main() {
    while(~scanf("%d", &n)) {
        int k = 3*n*n/4;
        printf("%d\n", k);
        for(int i = n-1; i >= 1; i -= 2) {
            for(int j = 1; j <= n; j ++) {
                if((i+j)%2 == 0) {
                    printf("1 %d %d 1\n", i, j);
                    printf("1 %d %d 1\n", i+1, j+1);
                    printf("2 %d %d 2\n", i, j);
                }
            }
        }
    }
    return 0;
}

L-Printer(暴力)

题目大意:有两层楼,上下楼需要 k k k时间,每层楼相邻位置移动花费 1 1 1时间,求某个位置到所有 1 1 1位置时间的最大值的最小值和最大值。

解题思路:暴力

AC_code:

#include
const int inf = 1e9+7;
using namespace std;
 
int n, k;
char s1[1010], s2[1010];
 
int main() {
    while(~scanf("%d%d", &n, &k)) {
        scanf("%s%s", s1, s2);
        int ans = inf, idx1, idx2;
        for(int i = 0; i < n; i ++) {
            int sum1 = 0, sum2 = 0;
            for(int j = 0; j < n; j ++) {
                if(s1[j] == '1') sum1 = max(sum1, abs(j-i)), sum2 = max(sum2, i+j+1+1+k);
            }
            for(int j = 0; j < n; j ++) {
                if(s2[j] == '1') sum2 = max(sum2, abs(j-i)), sum1 = max(sum1, i+j+1+1+k);
            }
            if(sum1 < ans) {
                idx1 = 2, idx2 = i+1;
                ans = sum1;
            }
            if(sum2 < ans) {
                idx1 = 1, idx2 = i+1;
                ans = sum2;
            }
        }
        printf("%d\n%d %d\n", ans, idx1, idx2);
    }
    return 0;
}

你可能感兴趣的:(ACM_gym刷题)