18.8.17 考试总结

高斯消元
【问题描述】
everlasting 觉得太无聊了,于是决定自己玩游戏!

他拿出了n 个小圆,第i 个的颜色是ai。接着他将这n 个小圆复制m 次并依次连接起来。

之后他发现其中有很多颜色相同的小圆,于是他决定:每有k 个连续颜色相同的小圆就将他们消去,

并将剩下的依次连接。(注意只会消除k个,即使有超过k 个)他将每次从头开始不断进行这个操作直到无法操作为止。

他想知道最后能剩下多少个小圆?

【输入格式】

从文件guass.in 中输入数据。

第一行三个正整数n,m,k,表示开始小圆的个数,复制次数和消除连续小圆
的个数。
第二行n 个正整数,第i 个数表示第i 个小圆的颜色。

【输出格式】

输出到文件guass.out 中。

一个整数,表示剩余小圆的个数。

这道题毫无算法可言.. 就是极端恶星的模拟模拟模拟 然后细节暴多 机房里面只有turuisen大佬花了三个小时搞出来了

然后就是lantx花了两个小时真 乱搞弄了95

我? 我一个样例都没过的错误程序还狗了40分...受宠若惊了

先把在一个循环里的连续k个相同都去掉,然后算出头和尾能消去的个数

,然后就是每个循环剩余长度*(m-1)+仅去掉同一循环的剩余数的个数。

为什么呢 画个图就清楚了

每个循环剩余长度就是天蓝色设为x 最后消去之后还剩下m * x 但是两头的湖蓝色并没有消去

湖蓝色长度和就为一整块长度 - x 所以总共长度就是一整块长度 + (m - 1) * x

但是可能他搞完之后中间的天蓝色凑到一起也可以被消去 特判一下就好了

代码

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

typedef long long ll;
const int N = 1e6 + 5;
int x,s[N][2],n,m,p,cnt,tot;
ll ans;

int main( ) {

    freopen("guass.in","r",stdin);
    freopen("guass.out","w",stdout);
    scanf("%d%d%d",& n,& m,& p);
    for(int i = 1;i <= n;i ++) {
        scanf("%d",& x);
        if(! cnt || s[cnt][0] != x) {
            s[++ cnt][0] = x;
            s[cnt][1] = 1;
        }
        else s[cnt][1] ++;
        if(s[cnt][1] == p) s[cnt][1] = 0,cnt --;
    }
    for(int i = 1;i <= cnt;i ++) tot += s[i][1];
    int head = 1,tail = cnt;
    while(head < tail && s[head][0] == s[tail][0]) {
        if((s[head][1] + s[tail][1]) % p == 0) head ++,tail --;
        else {
            s[head][1] = (s[head][1] + s[tail][1]) % p;
            s[tail][1] = 0;
            break;
        }
    }
    if(head < tail) {
        for(int i = head;i <= tail;i ++) ans += (ll)s[i][1];
        ans = (ll)tot + 1LL * (m - 1) * ans;
    }
    else {
        if(s[head][1] * m % p == 0) ans = 0;
        else {
            ans = (ll)s[head][1];
            ans = (ll)tot + (ll)ans * (m - 1);
            ans -= 1LL * s[head][1] * m - 1LL * s[head][1] * m % p;
        }
    }
    printf("%I64d",ans);
}

糖果镇
【问题描述】
yyc 来到了糖果镇,她在这里能得到好多好吃的棒棒糖^_^,糖果镇的道路是一个n*m方阵,

在每个点上会有一些糖果,但是yyc每到一个点都会拿走所有的糖果,并且她只能向下走或者向右走,

且要从起点(1,1)走到终点(n,m),否则她会被留在糖果镇贪婪的恶魔吃掉⊙o⊙

当然,为了不让一个人占有过多的棒棒糖,糖果镇决定让每一个人最后得到的糖果数对于p取模。

所以现在yyc想请你帮她算一算这样她最多能得到多少的棒棒糖。

【输入格式】
从文件candy.in中输入数据。

第一行3个正整数n,m,p

接下来m行,每行n个数,第i行第j列a[i][j]表示该点的糖果数。

【输出格式】
输出到文件candy.out中。

一行,一个整数,表示答案。

当m<=2时,枚举分界点,预处理前缀和就好了

对于另外20%,直接dp[i][j]表示到点i,j的最优答案

对于100%,我们可以枚举第二次下行的分界点,然后我们可以统计出第一次下行的影响,

就是第一行到x的前缀和减去第二行到x-1的前缀和,第二行就是到枚举端点的前缀和,

这样在已知第二次下行的分界点时,第二行和第三行的贡献是已知的,

我们只要找到模意义下第一行的最优贡献就好了,这个就可以用set维护,找前驱。

(如果是枚举第一行的分界点那就是倒着搞 这样子就能保证我能用的存了 不能用的没存)

第一次用set感觉好高级啊 z大爷tql...!!!!

代码

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

typedef long long ll;
const int N = 1e5 + 5;
int n,m;
ll a[4][N],S,ans = 0,dp[4][N],sum[4][N],mod;
set<ll>st;
set<ll>::iterator it;

void solve1( ) {

    if(m == 1) ans = S % mod;
    else {
        for(int i = 1;i <= n;i ++)
          ans = max(ans,(sum[1][i] % mod + sum[2][n] - sum[2][i - 1]) % mod);
    }
}

void solve2( ) {

    for(int i = 1;i <= m;i ++)
      for(int j = 1;j <= n;j ++)
        dp[i][j] = max(dp[i][j],max(dp[i][j - 1],dp[i - 1][j])) + a[i][j];
    ans = dp[m][n];
}

void solve3( ) {

    for(int i = 1;i <= n;i ++) {
        ll x = (sum[1][i] - sum[2][i - 1] + mod) % mod;
        st.insert(x);
        ll m = (sum[3][n] - sum[3][i - 1] + sum[2][i] + mod) % mod;
        it = st.lower_bound(mod - m);
        if(it != st.begin( )) it --;
        ans = max(ans,(m + (*it)) % mod);
        it = st.end( );
        if(it != st.begin( )) it --;
        ans = max(ans,(m + (*it)) % mod);
    }
}

ll read( ) {

    ll ans = 0,t = 1;
    char x; x = getchar( );
    while(x < ‘0‘ || x > ‘9‘) {
        if(x == ‘-‘) t = -1;
        x = getchar( );
    }
    while(x >= ‘0‘ && x <= ‘9‘) {
        ans = ans * 10 + x - ‘0‘;
        x = getchar( );
    }
    return ans * t;
}

int main( ) {

    freopen("candy.in","r",stdin);
    freopen("candy.out","w",stdout);
    scanf("%d%d%I64d",& n,& m,& mod);
    for(int i = 1;i <= m;i ++) {
        for(int j = 1;j <= n;j ++) {
            a[i][j] = read( );
            sum[i][j] = sum[i][j - 1] + a[i][j];
        }
    }
    S = 0;
    for(int i = 1;i <= m;i ++) S += sum[i][n];
    if(m <= 2) solve1( );
    else if(S < mod) solve2( );
    else solve3( );
    printf("%I64d",ans);
}

游戏
【问题描述】 小明和小刚正在玩如下的游戏。首先小明画一个有N个顶点,M条边的有向图。

然后小刚试着摧毁它。在一次操作中他可以找到图中的一个点,并且删除它所有的入边或所有的出边。

小明给每个点定义了两个值:Wi+和Wi-。如果小刚删除了第i个点所有的入边他要给小明付Wi+元,

如果他删除了所有的出边就需要给小明付Wi-元。 找到小刚删除图中所有边需要的最小花费。

【输入格式】
从文件game.in中输入数据。
第一行有两个数N,M(1<=N<=100,1<=M<=5000)。
第二行有N个整数,描述了N个点的Wi+,同样的第三行是这N个点的Wi-。所有的费用都是正数并且不超过10^6。接下来的M行每行有两个数,代表有向图中相应的一条边。

【输出格式】
输出到文件game.out中。 输出一行一个整数,即小刚的最小花费。

这道题是原题了 感觉讲了好多次

通过题目可以发现,我们要求的就是在使用最小的点权的情况下选中所有边,

每条边都可以被他的两个端点选中,且出入点权可能不同,于是就可以发现这是一个最小点权覆盖。

建边:

1>   先建立虚拟源S和汇T,把每个点拆成两个,ia,ib。

2>   从S向ia连一条流量为wi-的边,从ib向T连一条流量为wi+的边。

3>   原图中的边从ua向vb连一条流量无穷大的边。

4>  然后跑最大流即可。

拆一拆点 其乐无穷 还有这道题好像必须要加当前弧优化和delta优化 不然看姜sir就活生生被卡成40分

代码

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

const int N = 500;
const int M = 1e5;
int n,m,src,sink,w,tot = 1,ans,q[N],h[N];
int head[N],nex[M],tov[M],f[M],dis[N];
bool vis[N];

void add(int u,int v,int w) {

    tot ++;
    nex[tot] = head[u];
    tov[tot] = v;
    f[tot] = w;
    head[u] = tot;
    tot ++;
    nex[tot] = head[v];
    tov[tot] = u;
    f[tot] = 0;
    head[v] = tot;
}

void add_edge( ) {

    src = 1,sink = 2 * n + 2;
    for(int i = n + 2;i <= 2 * n + 1;i ++) {
        scanf("%d",& w);
        add(i,sink,w);
    }
    for(int i = 2;i <= n + 1;i ++) {
        scanf("%d",& w);
        add(src,i,w);
    }
    for(int i = 1;i <= m;i ++) {
        int u,v;
        scanf("%d%d",& u,& v);
        add(1 + u,1 + n + v,oo);
    }
}

bool bfs( ) {

    memset(vis,0,sizeof(vis));
    memset(dis,0,sizeof(dis));
    int h = 0,t = 0;
    q[++ t] = src;
    vis[src] = true;
    while(h < t) {
        int u = q[++ h];
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(f[i] && (! vis[v])) {
                vis[v] = true;
                dis[v] = dis[u] + 1;
                q[++ t] = v;
            }
        }
    }
    return vis[sink];
}

int dfs(int u,int delta) {

    if(u == sink) return delta;
    int res = 0;
    for(int & i = h[u];i;i = nex[i]) {
        int v = tov[i];
        if(f[i] && delta && dis[v] == dis[u] + 1) {
            int del = dfs(v,min(delta,f[i]));
            res += del;
            delta -=del;
            f[i] -= del;
            f[i ^ 1] += del;
        }
    }
    return res;
}

int main( ) {

    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    scanf("%d%d",& n,& m);
    add_edge( );
    while(bfs( )) {
        for(int i = src;i <= sink;i ++) h[i] = head[i];
        ans += dfs(src,oo);
    }
    printf("%d",ans);
    return 0;
}

今天竟然是rank 2 有点小高兴..w

原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9494474.html

时间: 2024-10-08 17:36:51

18.8.17 考试总结的相关文章

18.10.17 考试总结

今天心态崩崩,,,.. 这道题比较简单 因为每一位是单独对答案产生贡献的 所以枚举每一位 用数位dp求出该位是$1$的数量 在求出该位是$0$的 那么答案就是那一位对应的二的幂次再乘以$num1 * num0 * 2$ 每一对会产生两次贡献 代码 #include <bits/stdc++.h> #define rg register #define il inline using namespace std; typedef long long ll; const ll MOD = 1e9

18.9.17 poj2492 A Bug&#39;s Life

描述 BackgroundProfessor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their

18.12.17 POJ 1569 Myacm Triangles

描述 There has been considerable archeological work on the ancient Myacm culture. Many artifacts have been found in what have been called power fields: a fairly small area, less than 100 meters square where there are from four to fifteen tall monuments

18.12.17 POJ 1269 Intersecting Lines

描述 We all know that a pair of distinct points on a plane defines a line and that a pair of lines on a plane will intersect in one of three ways: 1) no intersection because they are parallel, 2) intersect in a line because they are on top of one anoth

18.1.1考试

今天考的题目都是DP,本来以为会有什么图论...根据今天题目比较水的原因,我直接放解题报告,大家应该可以看得懂.. T1 Source #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> using namespace std; const int maxn=10000+10,maxm=10010; #define file(a) freopen(a".i

18.8.13 考试总结

1.1 问题描述请构造一颗n 个节点的树,使得其价值最大.f(d) 表示树上,度数为d 的一个点能够获取的价值.这棵树的价值为Σni=1 f(di)di 表示第i 个点的度数1.2 输入第一行一个整数T,接下来T 组数据,每组数据输入两行.第一行输入整数n.第二行输入n ?? 1 个整数f(i) 代表f(1) f(n ?? 1).1.3 输出对于每组数据输出一行,为能够构造的树的最大价值. 一开始我以为是一道树形dp... 考完了才知道原来这个是一道背包问题 n个节点 总共有2n - 2个度数

18.8.26 考试总结

我真的服了 我考试的时候这道题题都是读错了的 交了个挖挖机结果还狗了20分.. 这道题是一道找规律的题 看完题很显然能够发现我们可以将相同颜色的连通块缩点 因为同一个联通块的可以一次操作全部变成另外一种颜色 所以就缩点就好了.. 对于缩点后的一条链 每次我们可以将一个点变色 那么和他相邻的点就和他颜色一样 然后就再次缩点 所以每次链的长度都可以 -2 所以说最后缩点的次数就是  点的个数 / 2  (下取整) 但是这是对于一条链 那么对于一棵树而言呢? 其实是一样的 因为每次搞这个操作 我们都可

18.9.22 考试总结

这道题一看就是可持久化并查集 然后我就愉快的yy了一波 还是错掉了qwqwqwqwq 方法是对的 就是我每次在树上查询$fa$的时候我还压缩了路径 导致这玩意空间炸掉了 所以要保证时间复杂度 就启发式合并 也就是$size$小的往$size$大的搞 这样子就保证每次合并的时候连通块元素个数每次至少乘以$2$ 也就是保证了层数是$log$级的 代码 #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int n

18.1.17割点

原题链接:https://www.luogu.org/problemnew/show/3388 割点的tarjan算法:选定一个节点为根节点,遍历整个图,形成一棵树 根节点若有两个子树,则其一定是割点.对于不是根节点的节点,维护两个数组dfn与lowdfs[u]表示节点u第几个被访问,low[u]表示节点u最早能回溯到的边对于边(u, v),如果low[v]>=dfn[u],此时u就是割点.边(u,v),如果v未访问过,dfs之后,更新:low[u]=min(low[u],low[v]):访问过