CF1214

C题WA3发的菜鸡还能涨分

A

发现货币面值都是倍数关系,直接暴力枚举第第一种换了多少个更新答案就好了

B

按照题意模拟

C

首先,左括号的数量不等于有括号的数量一定无解

想等的话在括号匹配的过程有在某一时刻栈中存在两个或者以上的右括号便无解

错误原因:思路出了点小问题

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 5e5 + 3;
char s[N];
int sta[N];
int n;
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
int sum1;
int tot;
int main(){
n = read();
scanf("%s",s + 1);
for(int i = 1;i <= n;++i)
sum1 += (s[i] == ')');
if(sum1 != n - sum1){printf("No\n");return 0;}
for(int i = 1;i <= n;++i){
if(s[i] == '(') tot++;
else{
tot--;
if(tot <= -2){printf("No\n");return 0;}
}
}
if(tot >= 2) printf("No\n");
else printf("Yes\n");
return 0;
}

D

我也不知道自己写了个什么算法,但是它过掉了

考虑这种情况

如图,如果黄色障碍点的话

绿色的点不管是不是障碍,都可以当做障碍去看

之后我们扩展一下

设\(dis_{x,y}\)表示把经过\((x,y)\)发出的路径全部搞死的最小代价

如果\((x,y)\)是空地并且\((x,y)\)和\((1,1)\)联通

\(dis_{x,y} = (dis_{x + 1,y} > 0 ) + (dis_{x,y + 1} > 0)\)

否则\(dis_{x,y} = 0\)

我们需要倒着这么转移

当然边界情况需要判断一下

之后我们就可以枚举所有的对角线的dis值之和

取个\(min\)

但是,答案如果涨这个样子怎么办

很明显我们只需要堵住一个格子

但是很明显答案不是对角线

想想我们\(dp\)的过程,我们已经把下面的贡献转移到了上面

所以他的dis数组是这样的,

\[\begin{array}{|c|c|c|c|}\hline 1 & {2} & {1} & {0} \\ \hline 1 & {1} & {1} & {0} \\ \hline 0 & {0} & {1} & {0} \\ \hline 0 & {0} & {1} & {1} \\ \hline\end{array}
\]

然后这样发现答案也是1

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 1e6 + 3;
string s[N];
bool book[N << 1];
int n,m;
int dis[N << 1];
int dx[2] = {0,1},dy[2] = {1,0};
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline int bfs(){
memset(book,0,sizeof(book));
book[0] = 1;
queue <int> q;q.push(0);
while(!q.empty()){
int k = q.front();q.pop();
int x = k / m,y = k % m;
for(int i = 0;i < 2;++i){
int xx = x + dx[i],yy = y + dy[i];
// if(book[]) continue;
if(xx < 0 || xx >= n || yy < 0 || yy >= m || book[xx * m + yy] || s[xx][yy] == '#') continue;
int z = xx * m + yy;
q.push(xx * m + yy);
book[xx * m + yy] = 1;
}
}
}
int sum[N << 1];
int main(){
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i = 0;i < n;++i) cin >> s[i];
memset(dis,0x3f,sizeof(dis));
for(int i = 0;i < n;++i){
for(int j = 0;j < m;++j)
if(s[i][j] == '#') dis[i * m + j] = 0;
}
bfs();
dis[n * m - 1] = 1;
for(int i = n - 1;i >= 0;--i){
for(int j = m - 1;j >= 0;--j){
int x = i * m + j;
if(s[i][j] == '#') continue;
if(!book[i * m + j]){dis[x] = 0;continue;}
if(i == n - 1 && j == m - 1) continue;
if(i == n - 1) dis[x] = (dis[x + 1] > 0);
else if(j == m - 1) dis[x] = (dis[x + m] > 0);
else dis[x] = (dis[x + 1] > 0) + (dis[x + m] > 0);
}
}
int ans = 2;
for(int i = 0;i < n;++i){
for(int j = 0;j < m;++j){
sum[i + j] += (dis[i * m + j] > 0);
}
}
for(int i = 1;i <= n + m - 2 - 1;++i) ans = min(ans,sum[i]);
// ans = min(ans,sum[1]);
printf("%d\n",ans);
return 0;
}

update:赛后被刁神吊打了Orz

这道题完全不需要这么麻烦

因为搜索出所有可能到达的点之后,维护两个指针

一个能向下走就向下走,另外一个能向右走就向右走

如果这两个路径有交点,那么一定是所有路径的必经点

直接把这个点删掉就好了QAQ

E

构造题

我们先把所有距离从大到小排序

因为题目保证有解

把所有的点对按照\(d\)排序之后,我们发现一个神奇的性质

对于排序完之后的数组的某一下标\(i\)

\(i + 1\)的距离在链上对应的点最大在\(i\)对应的点的右边

可能有些不好理解,结合图片理解一下

我们先构造一条这样的链

因为有距离\(d\)排完序之后不增的限制,所以\(i + 1\)对应为位置最多如图红色所示

这样我们暴力填上面的,对应的点就向这个样子挂下去

但是这样可能有个问题

如果上面的链的长度大于了\(n\)怎么办?

不用担心

每一个空点前面的点一定有儿子

因为有距离\(d\)的限制

所以我只直接把前面节点的儿子放到这个空点上就好了

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 1e6 + 3;
vector <int> G[N];
int ans[N];
int sum[N];
int tot;
int n;
struct node{
int id;
int val;
}a[N];
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline bool cmp(node x,node y){
return x.val > y.val;
}
int main(){
n = read();
for(int i = 1;i <= n;++i){
a[i].val = read();
a[i].id = i;
}
sort(a + 1,a + n + 1,cmp);
int len = 0;
for(int i = 1;i <= n;++i){
G[i + a[i].val - 1].push_back((a[i].id << 1) - 1);
sum[i] = a[i].id << 1;
len = max(len,i + a[i].val - 1);
}
for(int i = 1;i <= len;++i){
if(!sum[i]){
int x = G[i - 1].size();
sum[i] = G[i - 1][x - 1];
G[i - 1].pop_back();
}
}
for(int i = 1;i <= len;++i){
if(i != len) printf("%d %d\n",sum[i],sum[i + 1]);
for(int j = 0;j < (int)G[i].size();++j){
printf("%d %d\n",sum[i],G[i][j]);
}
}
return 0;
}

F

咕咕咕

经过\(smy\)和\(wqy\)的讲解之后,终于来补题了 QAQ

首先,我们需要证明,最佳答案的匹配一应不会覆盖整个环

考虑如果没有被覆盖的点,说明可能出现了下面的情况

红色的是同一类,黄色的同一类,最左右还有一个环没有画出来

你发现

把所有匹配的位置对应叮会更优(还有个环没画出来)

然后我们就可以枚举断点的位置

让所有的点都在这个位置一侧去匹配

我们想

枚举断点后断点左边的坐标要整体\(+m\)

对于一个人\(x_i\)和办公室\(y_i\)

他们匹配的代价是\(|x_i - y_i|\),

发现\(x_i\),和\(y_i\)的系数为\(1,-1\)

我们要求的实际上是

\[\sum_{i = 1}^n a _i x_i +b_iy_i
\]

\(a_i,b_i\)是他们贡献的系数

贡献的系数发现只取决于他是和左边的点匹配还是和右边的点去匹配

我们设\(d_i\)来表示这个东西

点上面的数就是他们\(d\)的值,然后我们发现,\(d >=0\)的坐标对答案的贡献都是\(-1\)

\(d < 0\)的坐标对答案啊的贡献都是\(1\)

这样的话,我们发现每一次枚举断点,消掉部分的会整体-1,对立部分整体+1

这个东西我们直接用指针去维护

维护到现在应该哪一部分的贡献将会改变

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 1e6 + 3;
const LL INF = 1e15;
struct node{
LL x;
int id;
int type;
}a[N];
int d[N];
LL _suma[N],_sumb[N];
LL *suma = _suma + 400000,*sumb = _sumb + 400000;
int n,m;
queue <int> q1,q2;
int as[N];
LL ans = INF,res;
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline bool cmp(node xx,node yy){
return xx.x < yy.x;
}
int main(){
m = read(),n = read();
for(int i = 1;i <= n;++i){
a[i].x = read();
a[i].id = i;
a[i].type = 1;
}
for(int i = 1;i <= n;++i){
a[i + n].x = read();
a[i + n].id = i;
a[i + n].type = 2;
}
sort(a + 1,a + 2 * n + 1,cmp);
int na = 0,nb = 0;
for(int i = 1;i <= 2 * n;++i){
if(a[i].type == 1){
d[i] = na - nb;
suma[na - nb] += a[i].x;
na++;
}
else{
d[i] = nb - na;
sumb[nb - na] += a[i].x;
nb++;
}
}
int pos = 0;
for(int i = -n;i <= n;++i){
if(i >= 0) res -= suma[i] + sumb[i];
else res += suma[i] + sumb[i];
// printf("i:%d suma:%lld sumb:%lld\n",i,suma[i],sumb[i]);
}
// for(int i = 1;i <= 2 * n;++i) printf("%d\n",d[i]);
// printf("%d\n",res);
ans = res;
int ta = 0,tb = 0;
for(int i = 1;i <= 2 * n;++i){
// printf("%d %d %d\n",i,ta,tb);
if(a[i].type == 1){
res += suma[ta];
suma[d[i]] += m;
res += suma[ta];
ta++,tb--;
res -= sumb[tb] * 2;
}
else{
res += sumb[tb];
sumb[d[i]] += m;
res += sumb[tb];
ta--,tb++;
res -= suma[ta] * 2;
}
if(res < ans){
pos = i;
ans = res;
}
}
printf("%lld\n",ans);
for(int i = pos + 1;i <= 2 * n;++i){
if(a[i].type == 1){
if(!q2.empty()) as[a[i].id] = q2.front(),q2.pop();
else q1.push(a[i].id);
}
else{
if(!q1.empty()) as[q1.front()] = a[i].id,q1.pop();
else q2.push(a[i].id);
}
}
for(int i = 1;i <= pos;++i){
if(a[i].type == 1){
if(!q2.empty()) as[a[i].id] = q2.front(),q2.pop();
else q1.push(a[i].id);
}
else{
if(!q1.empty()) as[q1.front()] = a[i].id,q1.pop();
else q2.push(a[i].id);
}
}
for(int i = 1;i <= n;++i) printf("%d ",as[i]);
return 0;
}

CF1214的更多相关文章

  1. CF1214题解

    D 因为可以用贡献2把起点终点堵掉,所以答案为0/1/2 0/2简单 1:方格可以理解为分层图,考虑每个能到达终点,起点能到达其的点,标记一下,对角线如果仅存在1则为必经之路 E \(d_i\le n ...

  2. 你还在为了JVM而烦恼么?(内存结构和垃圾回收算法)

    ​ 做JAVA也有接近2年的时间了,公司的leader说,做JAVA,三年是个坎,如果过了三年你还没有去研究JVM的话,那么你这个程序员只能是板砖的工具了.恰逢辞职,来个JVM的解析可好? JVM是J ...

  3. VP记录

    预计在最后的日子里适量VP 简单记录一下 CF 1037 Link 上来秒了ABCD,很快啊 A是二进制拆分,B是一眼贪心,C是一个非常简单且好写的dp D把边遍历顺序按照所需的bfs顺序排序,最后比 ...

随机推荐

  1. 【JZOJ4886】【NOIP2016提高A组集训第13场11.11】字符串

    题目描述 某日mhy12345在教同学们写helloworld,要求同学们用程序输出一个给定长度的字符串,然而发现有些人输出了一些"危险"的东西,所以mhy12345想知道对于任意 ...

  2. poj 2236【并查集】

    poj 2236 Description An earthquake takes place in Southeast Asia. The ACM (Asia Cooperated Medical t ...

  3. poj3294 后缀数组

    后缀数组多个字符串问题. 先求出height[]数组,然后二分求最大的长度. 但是条件需要改变.如果出现次数大于一般那就满足.然后就要解决如何判断那一段属于其中一个字符串. 所以先处理出长度.并且不断 ...

  4. ANSI编码方式转化为UTF-8方式

    说明: 记事本txt有四种编码方式,分别为:UTF-8.ANSI.Unicode和Unicode big endian,当进行写操作,创建的txt编码格式,与写入汉字的编码方式相同:如果写入的汉字是不 ...

  5. Gartner:阿里云位列全球云数据库市场份额前三,数据库未来需上云

    近日,国际权威研究机构Gartner发布 <The Future of the Database Management System (DBMS) Market Is Cloud>报告,鲜 ...

  6. dnspython

    dnspython 一个Python实现的一个DNS工具包,利用其查询功能来实现dns的服务监控及解析结果的校验. 安装 pip install dnspython 解析域名为IP from dns ...

  7. Myeclipse 设置默认注释

    windows-->preference-->Java-->Code Style-->Code Templates code-->New Java files ${fil ...

  8. oracle函数 to_single_byte(c1)

    [功能]将字符串中的全角转化为半角 [参数]c1,字符型 [返回]字符串 [示例] SQL> select to_multi_byte('高A') text from dual; test -- ...

  9. Java练习 SDUT-1171_保留整数

    C语言实验--保留整数 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 输入一个字符串str1,把其中的连续非数字的字符 ...

  10. 【SDOI2015】bzoj3990 排序

    A. 排序 题目描述 输入格式 输出格式 一行,一个整数,表示可以将数组A从小到大排序的不同的操作序列的个数. 样例 样例输入 3 7 8 5 6 1 2 4 3 样例输出 6 数据范围与提示 对于3 ...