题解

排队

20% 1≤n≤20,1≤x,hi≤201\le n\le 20, 1\le x,h_i\le 201≤n≤20,1≤x,hi​≤20 随便暴力

50% 1≤n≤2000,1≤x,hi≤1e91\le n \le 2000, 1\le x,h_i\le 1e91≤n≤2000,1≤x,hi​≤1e9 枚举把哪个定成中位数

100% 1≤n≤2e5,1≤x,hi≤1e91\le n\le 2e5,1\le x,h_i\le 1e91≤n≤2e5,1≤x,hi​≤1e9

先把高度排序。贪心地修改。最好的方法是把最中间的人变成xxx。那么就把左边比他高的或者右边比他矮的都改成xxx就好啦。

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int MAXN = 200005;
int n, x, h[MAXN];
LL Ans;
int main()
{
scanf("%d%d", &n, &x);
for(int i = 1; i <= n; i++)
scanf("%d", &h[i]);
sort(h + 1, h + n + 1);
int l = n>>1, r = (n>>1)+2;
Ans = abs(h[(n>>1)+1]-x), h[(n>>1)+1] = x;
while(l >= 1 && r <= n)
{
Ans += max(h[l]-h[l+1], 0), h[l] = min(h[l], h[l+1]); l--;
Ans += max(h[r-1]-h[r], 0), h[r] = max(h[r], h[r-1]); r++;
}
printf("%lld\n", Ans);
}

翘课

10% 2≤n,m≤500,k=12\le n,m\le 500,k=12≤n,m≤500,k=1

20% 2≤n,m≤200000,k=12\le n,m\le 200000,k=12≤n,m≤200000,k=1 暴力

40% 2≤n,m≤2000,k=22\le n,m\le 2000,k=22≤n,m≤2000,k=2 找环

70% 2≤n,m≤2000,k&lt;n2\le n,m\le 2000,k&lt;n2≤n,m≤2000,k<n 每次跑点判断

100% 2≤n,m≤200000,1≤k&lt;n2\le n,m\le 200000,1\le k &lt;n2≤n,m≤200000,1≤k<n 倒着删

考虑先把所有边加进去,再倒过来删边。

对于每个点,我们考虑要不要删掉它,删掉它表示它连接着选中的点的度小于 K 了。删完它之后我们把它邻居的度都–,然后再看它的邻居要不要被删掉。这样预处理把每个点判一次要不要删。

之后删一条边也是删完就判断连接的两个点需不需要被删掉。因为每个点只会被删一次,所以整体还是O(n)的。

题目做法可能多样,但思路应该都是倒过来。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
const int MAXM = 200005;
inline void read(int &num)
{
char ch; int flag=1;
while(!isdigit(ch=getchar()))if(ch=='-')flag=-flag;
for(num=ch-'0';isdigit(ch=getchar());num=num*10+ch-'0');
num*=flag;
} int n, m, k, d[MAXN], Ans;
int fir[MAXN], to[MAXM<<1], nxt[MAXM<<1], cnt = 1; inline void Add(int u, int v)
{
to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt;
to[++cnt] = u; nxt[cnt] = fir[v]; fir[v] = cnt;
} bool flg[MAXN], used[MAXM<<1]; inline void del(int u)
{
if(!flg[u]) return;
flg[u] = 0; Ans--;
for(int i = fir[u]; i; i = nxt[i])
if(used[i^1] && (used[i^1] = 0, --d[to[i]] == k-1)) del(to[i]);
} bool vis[MAXN];
inline bool dfs1(int u)
{
if(vis[u]) return flg[u];
vis[u] = 1;
flg[u] = 1; Ans++;
for(int i = fir[u]; i; i = nxt[i])
if(dfs1(to[i]))
++d[u], used[i] = 1;
if(d[u] >= k) return 1;
del(u);
return 0;
}
int U[MAXM], V[MAXM], ans[MAXM];
int main ()
{
read(n), read(m), read(k);
for(int i = 1; i <= m; i++)
read(U[i]), read(V[i]), Add(U[i], V[i]);
for(int i = 1; i <= n; i++)
if(!vis[i]) dfs1(i);
for(int i = m; i >= 1; i--)
{
ans[i] = Ans;
if(used[i<<1] && (used[i<<1] = 0, --d[U[i]] == k-1)) del(U[i]);
if(used[i<<1|1] && (used[i<<1|1] = 0, --d[V[i]] == k-1)) del(V[i]);
}
for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);
}

运气大战

20% n≤10,q≤10n\le 10,q\le 10n≤10,q≤10 暴力

另30% n≤1000,q≤100n\le 1000,q\le 100n≤1000,q≤100 这个点我也不记得为什么放了,但看起来很多人只拿到了50所以可能还是有点意义的?

另20% n≤30000,q≤500n\le 30000,q\le 500n≤30000,q≤500 nq 的 dp

100% 2≤n≤30000,1≤q≤10000,1≤wi,ri≤1e6,1≤ai̸=bi≤n2\le n\le 30000,1\le q\le 10000,1\le w_i,r_i \le 1e6,1\le a_i\not=b_i \le n2≤n≤30000,1≤q≤10000,1≤wi​,ri​≤1e6,1≤ai​̸​=bi​≤n

由于排序不等式,我们尽量想顺序放。两边都排序。

由于 n 个不能配的干扰,又不能完全顺序放。

有个结论,最后匹配出第 i 个人的运气值是第 j 个的话,∣i−j∣≤2|i-j|\le2∣i−j∣≤2。这个结论从最小化逆序对的个数来看,自己把附近几个线连起来画一画证明一下。

这样就可以用 dp[i]表示到 i 为止所有配好的最优答案。计算的时候需要用到前三轮的答案然后讨论一下。这个是 O(nq)的,可以过70%。

用线段树记录区间答案。区间记录这样的信息:把这个区间前0-2个和后0-2个元素去掉的答案,用3x3的矩阵维护。这样复杂度是O(qlogn)。

由于出题人良心发现将时限改为3s,常数小的nq算法也能过1.5s左右,下贴AC代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int MAXN = 30005;
const LL INF = 1ll<<60;
inline void read(int &num)
{
char ch; int flag=1;
while(!isdigit(ch=getchar()))if(ch=='-')flag=-flag;
for(num=ch-'0';isdigit(ch=getchar());num=num*10+ch-'0');
num*=flag;
}
struct node
{
int val, id;
}A[MAXN], B[MAXN];
LL dp[MAXN], cost[MAXN][2];
int n, q, posa[MAXN], posb[MAXN]; inline LL Calc(int i, int j)
{
if(i < 1 || j < 1 || i > n || j > n || A[i].id == A[j].id) return -INF;
return 1ll * A[i].val * B[j].val;
} inline void Upd(int i)
{
if(i < 1 || i > n) return;
cost[i][0] = Calc(i, i+1) + Calc(i+1, i);
cost[i][1] = max(Calc(i, i+1) + Calc(i+1, i+2) + Calc(i+2, i),
Calc(i, i+2) + Calc(i+1, i) + Calc(i+2, i+1));
} inline bool cmp(node x, node y) { return x.val < y.val; } int main ()
{
read(n), read(q);
for(int i = 1; i <= n; i++) read(A[i].val), A[i].id = i;
for(int i = 1; i <= n; i++) read(B[i].val), B[i].id = i;
sort(A + 1, A + n + 1, cmp);
sort(B + 1, B + n + 1, cmp);
for(int i = 1; i <= n; i++) posa[A[i].id] = posb[B[i].id] = i, Upd(i);
int x, y;
while(q--)
{
read(x), read(y), swap(B[posb[x]].id, B[posb[y]].id), swap(posb[x], posb[y]);
for(int i = -2; i <= 0; i++)
Upd(posa[x+i]), Upd(posb[x+i]), Upd(posa[y+i]), Upd(posb[y+i]);
dp[n+1] = 0;
for(int i = n; i; i--)
{
dp[i] = max(dp[i+2] + cost[i][0], dp[i+3] + cost[i][1]);//i与i+1/2不能配对的情况一定会被另外更优的情况所取代,可能是此行的另一个max,可能是下一行的单独配对时取的max
if(A[i].id != B[i].id) dp[i] = max(dp[i], dp[i+1] + 1ll * A[i].val * B[i].val);
}
printf("%lld\n", dp[1]);
}
}

NOI.ac 模拟赛20181103 排队 翘课 运气大战的更多相关文章

  1. NOI.ac模拟赛20181021 ball sequence color

    T1 ball 可以发现每次推动球时,是将每个球的位置 −1-1−1 ,然后把最左边的球放到 P−1P-1P−1 处. 记个 −1-1−1 次数,再用set维护就好了. #include <bi ...

  2. [NOI P模拟赛] 传统艺能(子序列自动机、矩阵乘法,线段树)

    (2:00)OID:"完了,蓝屏了!"(代码全消失) 众人欢呼 OID:开机,"原题测试--" (30min later)OID 开始传统艺能: " ...

  3. 【NOI P模拟赛】最短路(树形DP,树的直径)

    题面 给定一棵 n n n 个结点的无根树,每条边的边权均为 1 1 1 . 树上标记有 m m m 个互不相同的关键点,小 A \tt A A 会在这 m m m 个点中等概率随机地选择 k k k ...

  4. noi.acNOIP模拟赛5-count

    题目链接 戳我 题意简述 你有一个n+1个数的序列,都是1~n,其中只有一个有重复,求每个长度的本质不同的子序列个数.\(mod 1e9+7\). sol 说起来也很简单,设相同的数出现的位置为\(l ...

  5. 【XJOI】【NOI考前模拟赛7】

    DP+卡常数+高精度/  计算几何+二分+判区间交/  凸包 首先感谢徐老师的慷慨,让蒟蒻有幸膜拜了学军的神题.祝NOI2015圆满成功 同时膜拜碾压了蒟蒻的众神QAQ 填填填 我的DP比较逗比……( ...

  6. 2018/3/13 noiρ[rəʊ]模拟赛 125分

    T1 60分暴力,水分也不会水,打表也不会打,正解是不可能写正解的,这辈子都写不出来正解的,虽然是zz题但是也拿不到分这样子. 正解:(啥?正解是sb组合数?这都他娘的想不到,真鸡儿丢人我自杀吧.) ...

  7. Newnode's NOI(P?)模拟赛 第二题 dp决策单调优化

    其实直接暴力O(n3)DP+O2O(n^3)DP+O_2O(n3)DP+O2​优化能过- CODE O(n3)O(n^3)O(n3) 先来个O(n3)O(n^3)O(n3)暴力DP(开了O2O_2O2 ...

  8. Newnode's NOI(P?)模拟赛 第三题 (主席树优化建图 + tarjan)

    题目/题解戳这里 这道题题目保证a,b,ca,b,ca,b,c各是一个排列-mdzz考场上想到正解但是没看到是排列,相等的情况想了半天-然后写了暴力60分走人- 由于两两间关系一定,那么就是一个竞赛图 ...

  9. 【NOI P模拟赛】奶油蛋糕塔(状压 DP)

    题面 数据范围 1 ≤ n ≤ 5 × 1 0 5 1\leq n\leq5\times10^5 1≤n≤5×105 . 题解 n ≤ 20 n\leq 20 n≤20 的状压应该都会吧,状态记录已经 ...

随机推荐

  1. python 之 前端开发( jQuery选择器、筛选器、样式操作、文本操作、属性操作、文档操作)

    11.5 jQuery 引入方式: 方式一:本地引入 <script src="jquery-3.3.1.min.js"></script> <scr ...

  2. 20191210-RobotFramework常见问题解决

    附加-问题解决 1.   执行robot用例的时候提示WebDriverException: Message: invalid argument: can't kill an exited proce ...

  3. 16 IO流(十三)——Object流 序列化与反序列化

    Object流.序列化与反序列化 Object流是将 可序列化的对象 进行序列化与反序列化的流. 可序列化的对象:使用关键字Serializable修饰,表示这个对象可以进行序列化与反序列化. 序列化 ...

  4. flask框架(七)——蓝图、请求上下文、g对象、信号、flask_session

    蓝图 作用:对程序进行目录结构划分 不使用蓝图情况下,自己分文件 目录结构: -templates -views -__init__.py -user.py -order.py -app.py app ...

  5. logrus 剖析之滚动日志

    在实际开发过程中,为了节省磁盘,日志需要按照时间或者大小维度进行切割分成多分,归档过期的日志,删除久远的日志.这个就是在日常开发中经常遇见的日志滚动(log rotation) 那么在 logrus ...

  6. Jmeter参数化(_csvread函数、CSV Data Set Config)

    方法一.Jmeter自带的函数助手——_CSVRead函数 1.数据准备:先在excel存储数据,保存格式选择csv格式.或在记事本存储数据,列之间用英文逗号分隔,保存为txt 2.使用_csvrea ...

  7. diverta 2019 Programming Contest 2

    A:签到. #include<bits/stdc++.h> using namespace std; #define ll long long #define inf 1000000010 ...

  8. Java调用WebService方法总结(7)--CXF调用WebService

    CXF = Celtix + XFire,继承了Celtix和XFire两大开源项目的精华,是一个开源的,全功能的,容易使用的WebService框架.文中所使用到的软件版本:Java 1.8.0_1 ...

  9. 【转载】 C#中List集合使用First()方法获取第一个元素

    在C#的List集合操作过程中,如果要获取List集合中的第一个元素对象,则一般会先通过获取到list[0]这种方式来获取第一个元素.其实在List集合中提供了获取最后一个元素的First()方法,调 ...

  10. GitHub上传文件夹

    1.输入自己的用户名和邮箱 为注册GitHub账号时所用的用户名和邮箱;我的用户名为“1997ST2016”,邮箱为“1324971964@qq.com ”. $ git config --globa ...