【洛谷】NOIP提高组模拟赛Day1【组合数学】【贪心+背包】【网络流判断是否满流以及流量方案】...

U41568 Agent1

题目背景

2018年11月17日,中国香港将会迎来一场XM大战,是世界各地的ENLIGHTENEDRESISTANCE开战的地点,某地 的ENLIGHTENED总部也想派Agent去参加这次的XM大战,与世界其他地方的ENLIGHTENED并肩作战。

题目描述

某地的ENLIGHTENED总部总部有NAgent,每个Agent的能力值互不相同,现在ENLIGHTENED行动指挥想要派出A,B两队Agent去参加XM大战。但是参加大战的两个队伍要满足两个要求:

  1. A队中能力最大的Agent的能力值要小于B队能力最弱的Agent的能力值。
  2. A,B两队都要有人参战。

并不一定所有的Agent都要去参加XM大战的,心急的ENLIGHTENED行动指挥想知道有多少种安排Agent参加大战的方案。由于答案可能很大,所以只需要你求出答案模(109+7)的值就可以了。

输入输出格式

输入格式:

 

输入仅一行,为一个整数N

 

输出格式:

 

输出答案模(109+7)的值。

 

输入输出样例

输入样例#1:  复制
3
输出样例#1:  复制
5
输入样例#2:  复制
6
输出样例#2:  复制
129

说明

对于20%的数据 N10

对于40%的数据 N103

对于60%的数据 N105

对于100%的数据 N109

背景影藏面目下的组合数学QAQ

也许我能推出来式子的组合式子就只能是$D1T1$水平了八QAQ

一开始找规律,发现要满足题目所给的条件,其实是分别在$n$个中找$2,3,4,5...n$个人的组合数方案在乘上$i-1$加起来。因为相当于要使小的连续一段在$A$,大的连续一段在$B$。

比如一共有5个人参赛,他们能力值就是$1、2、3、4、5$只能如下安排:

AB
12 3 4 5
1 23 4 5
1 2 34 5
1 2 3 45

 

只有(5-1)= 4种方法,再乘上$n$中选$5$个人的组合数。

得到公式:$ans=\sum_{i=2}^n{{C_n^i}*(i-1)}$,60分到手。

但是答案显然要$O(1)$得到($log$)。

发现每个单项式乘的$(i-1)$非常不美QAQ($zyl$dalao原话%%),所以把整个式子先加上一整个$\sum_{i=2}^n{C_n^i}$,$ans = \sum_{i=2}^n{C_n^i}-(2^n-1-n)$。

然后依然是$zyl$dalao以前传授的!

$i\sum_n^i = n\sum_{n-1}^{i-1}$,在n个人中选i个人组成一队,再在其中选择1个人作为队长的方案数等于现在n个人中选1个人当队长,再在剩下n-1个人中选i-1个人做组员的方案数。是唯一记得最清楚的组合方程了QAQ

所以$ans = n\sum_{i=2}^n{C_{n-1}^{i-1}}-(2^n-1-n)$,所以$ans=n(2^{n-1}-1)-(2^n-1-n)$。

结果交上去还wa了一次!QAQ取模啊取模姐姐!!

#include<bits/stdc++.h>
#define mod 1000000007
#define LL long long
using namespace std;

LL n;

LL mpow(LL a, LL b) {
    LL ans = 1;
    for(; b; b >>= 1, a = a * a % mod)
        if(b & 1)    ans = ans * a % mod;
    return ans;
}

int main() {
    scanf("%lld", &n);
    LL ans = ((n * mpow(2, n - 1) % mod - (mpow(2, n) - 1)) % mod + mod) % mod;
    printf("%lld", ans);
    return 0;
}

 

U41569 Portal1

题目背景

Agent获取资源有很多种方式,HACK就是其中的一中,侵入Portal可以获得很多有用的资源。ENLIGHTENED总部因为参加XM大战,只剩下一点点可用资源了,所以ENLIGHTENED行动指挥想要进行HACK活动,尽量增加库存。

题目描述

地图上有N个可以被HACKPortal,编号为1~N。HACKii号Portal需要时间T[i]秒,可以HACKC[i]库存的资源。可是只有有能量的Portal才可以HACK出资源。第ii号Portal在第D[i]秒时,能量就会消失殆尽。ENLIGHTEDED想知道,最多可以增加多少库存,并且按编号小到大输出需要HACKPortal的编号。

输入输出格式

输入格式:

 

第一行输入一个整数N

下接N行每行3个整数,T[i],D[i],C[i]

 

输出格式:

 

输出第一行为一个整数,最多可以增加多少库存。

第二行为一个整数,代表需要HACK多少个Portal

第三行按编号小到大输出需要HACKPortal的编号,若有多种HACK的方案输出其中一种即可。

 

输入输出样例

输入样例#1:  复制
3
5 6 5
1 8 2
2 7 3
输出样例#1:  复制
7
2
1 2

说明

对于20%的数据N5,T[i],C[i]5,D[i]10

对于40%的数据N20,T[i],C[i]10,D[i]100

对于60%的数据N50,T[i],C[i]15,D[i]1000

对于100%的数据 N≤100,T[i],C[i]≤20,D[i]≤2000

哭哭,本来以为自己从后往前DP完全正确的!结果后面状态还没更新,前面就更新了,爆错。

正确是先背包预处理出到达每天能获得最大的值,同时顺便处理出每个物品在当前时间点可不可以取。

因为背包的时候是正着枚举物品,虽然可能会覆盖,但是最后倒着取物品,就可以保证最后得到的物品是最优了。

#include<bits/stdc++.h>
using namespace std;

int n;

struct Node {
    int t, c, d, id;
} hack[105];
bool cmp(Node a, Node b) { return a.d < b.d; }

int dp[2005], vis[105][2005], QAQ[2006];
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)
        scanf("%d%d%d", &hack[i].t, &hack[i].d, &hack[i].c), hack[i].id = i;
    sort(hack + 1, hack + 1 + n, cmp);
    for(int i = 1; i <= n; i ++)
        for(int j = hack[i].d-1; j >= hack[i].t; j --) {
            if(dp[j] < dp[j-hack[i].t] + hack[i].c) {
                dp[j] = dp[j-hack[i].t] + hack[i].c;
                vis[i][j] = 1;
            }
        }
    int ans = 0;
    for(int i = 1; i <= hack[n].d; i ++)    if(dp[ans] < dp[i])    ans = i;
    printf("%d\n", dp[ans]);
    for(int i = n; i >= 1; i --)
        if(vis[i][ans]) QAQ[++QAQ[0]] = hack[i].id, ans -= hack[i].t;
    printf("%d\n", QAQ[0]);
    for(int i = QAQ[0]; i >= 1; i --)
        printf("%d ", QAQ[i]);
    return 0;
}

 

U41570 War1

题目背景

XM大战来临之际,ENLIGHTENED总部为了抵御RESISTANCE的进攻,调整了某地Portal的能量值,使得其可以经受更多的打击。

题目描述

ENLIGHTENED总部NPortal,编号为1~N,编号为ii的Portal初始能量值为A[i],在Portal之间有M条LINK,每条LINK着连接着两个不同Portal,被连接着的两个Portal可以相互传输能量,每个Portal最多总共只能向其连接着的Portal传输A[i]点能量,现在ENLIGHTENED行动指挥想让每第iPortal的能量值变为B[i],但他不知道着可不可行,所以找到了你,如果可行的话,需要你求出一种可行的能量传输方案。

能量只能之间传输不能间接传输

输入输出格式

输入格式:

 

第一行数两个个整数N,M。

第二行有N个整数,第i个整数代表A[i]

第三行有N个整数,第i个整数代表B[i]

下接M行,每行输入两个整数X,Y,代表编号为X的Portal到编号为Y的Portal有一条LINK

 

输出格式:

 

若有可行方案输出YES,并且下接N行,每行N个整数,第i行第j个数代表代表编号为i的Portal向编号为j的Portal传输的能量值。若i=j输出第i个Portal传输后留下的能量值。若有多种可行方案输出其中一种即可。

若无可行方案输出NO

 

输入输出样例

输入样例#1:  复制
3 2 
1 2 3
0 0 6
1 3
2 3
输出样例#1:  复制
3 2 
1 2 3
0 0 7
1 3
2 3

说明

对于20%的数据 N10

对于40%的数据 N25

对于60%的数据 N50

对于100%的数据 N≤100,M≤2*N,0≤A[i],B[i]≤100

数据范围以及题目条件暗示网络流。

将每个点拆点,分别向起点和终点连边,流量分别为$A[i]$和$B[i]$,中间首先自己向自己连流量为无穷的边,相连的两个点连边,因为是互相,所以有两条。直接跑最大流,判断流量是否和$B[i]$的和相等即是流满,在把中间反边的流量取出来即是两点间运输方案。

一开始一直T,发现是$Bfs$里面判断是用的$dep$,而起点的$dep$没有赋值,导致可能死循环。所以以后还是要习惯用$vis$判断!!

#include<bits/stdc++.h>
#define oo 0x3f3f3f3f
using namespace std;

void read(int &x) {
    x = 0; char ch = getchar();
    while(ch > '9' || ch < '0')    ch = getchar();
    while(ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
}

struct Node {
    int u, v, nex, f, rev;
    Node(int u = 0, int v = 0, int nex = 0, int f = 0, int rev = 0) :
        u(u), v(v), nex(nex), f(f), rev(rev) { }
} Edge[4000005];

int h[10005], stot = 1;
void add(int u, int v, int f) {
    ++stot;
    Edge[stot] = Node(u, v, h[u], f, stot + 1);
    h[u] = stot;
    ++stot;
    Edge[stot] = Node(v, u, h[v], 0, stot - 1);
    h[v] = stot;
}

int n, m, s, t;

int vis[505], dep[505];
queue < int > q;
bool bfs() {
    memset(vis, 0, sizeof(vis));
    memset(dep, 0, sizeof(dep));
    q.push(s);    vis[s] = 1; dep[s] = 1;
    while(!q.empty()) {
        int x = q.front(); q.pop();
        for(int i = h[x]; i; i = Edge[i].nex) {
            int v = Edge[i].v;
            if(!dep[v] && Edge[i].f) {
                dep[v] = dep[x] + 1;
                vis[v] = 1;
                q.push(v);
            }
        }
    }
    return vis[t];
}

int dfs(int u, int delta) {
    if(u == t || delta == 0) return delta;
    int res = 0;
    for(int i = h[u]; i && delta; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(dep[v] == dep[u] + 1 && Edge[i].f) {
            int dd = dfs(v, min(delta, Edge[i].f));
            Edge[i].f -= dd;
            Edge[i^1].f += dd;
            res += dd;
            delta -= dd;
        }
    }
    return res;
}

int sum;
int mp[105][105];
int main() {
    scanf("%d%d", &n, &m);
    int ans = 0;
    s = 2 * n + 2, t = 2 * n + 1;
    for(int i = 1; i <= n; i ++) {
        int a;
        read(a);
        add(s, i, a);
        ans += a;
    }
    for(int i = 1; i <= n; i ++) {
        int b;
        read(b);
        add(i + n, t, b);
        sum += b;
    }
    for(int i = 1; i <= n; i ++)    add(i, i + n, oo);
    for(int i = 1; i <= m; i ++) {
        int a, b;
        read(a); read(b);
        add(a, b + n, oo);
        add(b, a + n, oo);
    }
    if(ans != sum) {
        printf("NO");
        return 0;
    }
    ans = 0;
    while(bfs()) ans += dfs(s, oo);
    if(ans != sum)    printf("NO");
    else {
        printf("YES\n");
        for(int x = 1; x <= n; x ++)
            for(int i = h[x]; i; i = Edge[i].nex) {
                int v = Edge[i].v;
                if(v > n)    mp[x][v-n] = Edge[Edge[i].rev].f;
            }
        for(int i = 1; i <= n; i ++) {
            for(int j = 1; j < n; j ++)    printf("%d ", mp[i][j]);
            printf("%d\n", mp[i][n]);
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/wans-caesar-02111007/p/9745308.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值