A

假设只有一个连通块,任选一个点入队,按bfs/dfs序删除即可.

trick: 要考虑有多个连通块的情况,不一定无解.

 #define rep(i,n) for(int i=0 ; i<(n) ; i++)
#define mid ((l+r)>>1)
#define ls ((rt<<1))
#define rs ((rt<<1)+1)
#define maxn 510
char g[maxn][maxn];
bool vis[maxn][maxn];
int n,m,k;
struct node{
int r,c;
};queue<node> q;
struct src{
int size;
vector<node> ord;
};vector<src> p; int dr[] = {,-,,};
int dc[] = {,,,-}; src bfs(int r,int c) {
src t;
t.ord.clear();
q.push((node){r,c});
vis[r][c]=true;
while (!q.empty()) {
node cur = q.front(),nxt; q.pop();
t.ord.push_back(cur);
rep (i,) {
nxt.r = cur.r+dr[i];
nxt.c = cur.c+dc[i];
if (nxt.r<||nxt.r>=n) continue;
if (nxt.c<||nxt.c>=m) continue;
if (g[nxt.r][nxt.c]!='.') continue;
if (!vis[nxt.r][nxt.c]) {
vis[nxt.r][nxt.c]=true;
q.push(nxt);
}
}
}
t.size = t.ord.size();
return t;
} bool cmp(src a, src b) {
return a.size < b.size;
} void del(src t) {
for (int i=(int)t.ord.size()- ; i>= && k; i-- ) {
node cur = t.ord[i];
g[cur.r][cur.c] = 'X';
k--;
}
} int main() {
// freopen("test","r",stdin);
scanf("%d%d%d",&n,&m,&k);
rep (i,n) scanf("%s",g[i]);
rep (i,n) rep (j,m) {
if (g[i][j]=='.' && !vis[i][j]) {
p.push_back(bfs(i,j));
}
}
sort(p.begin(),p.end(),cmp);
rep (i,(int)p.size()) {
if (!k) break;
del(p[i]);
}
rep (i,n) printf("%s\n",g[i]);
return ;
}

B

二分答案,然后对于给定的答案t,当然是让能力足够的student解决尽量多的问题,然后问题就转化为:

  对于给定的ai,找到bj>=ai中c最小的元素

排序后用堆即可.

 #define rep(i,n) for(int i=0 ; i<(n) ; i++ )
#define ls ((rt)<<1)
#define rs (((rt)<<1)+1)
#define mid ((l+r)>>1)
#define maxn 100010
#define INF 1000000100
struct node{
int b,c,id;
bool operator < (const node &x) const{
return c>x.c;
}
};node stu[maxn];
int a[maxn],n,m,s,rcd[maxn],idx[maxn]; priority_queue<node> q; bool cmp(node x,node y) {
return x.b>y.b;
} bool cmpa(int x,int y) {
return a[x]<a[y];
} void input() {
scanf("%d%d%d",&n,&m,&s);
rep (i,m) scanf("%d",a+i);
rep (i,n) scanf("%d",&stu[i].b);
rep (i,n) scanf("%d",&stu[i].c);
rep (i,n) stu[i].id = i+;
rep (i,m) idx[i]=i;
stu[n] = (node){INF,INF,n};
sort(stu,stu+n+,cmp);
sort(idx,idx+m,cmpa);
} int check(int t) {
while (!q.empty()) q.pop();
int cnts=;
for (int i=m-,j= ; i>= ; i-- ) {
while (j<=n && stu[j].b>=a[idx[i]]) {
q.push(stu[j++]);
}
node cur = q.top(); q.pop();
cnts += cur.c;
if (cnts>s) return ;
rep (j,t) {
if (i-j>=) rcd[idx[i-j]] = cur.id;
else return ;
}
i -= (t-);
}
return ;
} int binsearch(int l,int r) {
while (true) {
if (l+>=r) {
if (check(l)) return l;
if (check(r)) return r;
return -;
}
if (check(mid)) r=mid;
else l=mid+;
}
} int main(){
// freopen("test.txt","r",stdin);
input();
int ans = binsearch(,m);
if (ans==-) printf("NO\n");
else {
printf("YES\n");
rep (i,m) printf("%d%c",rcd[i],i==m-?'\n':' ');
}
return ;
}

C

很容易地想到一个贪心做法:

  将同一个队的连续选择一起考虑,那么可以先进行p操作,再进行b操作;

  p操作当然选可选中体力最大的,b操作可以限制下一轮选择时对方体力增长,因此应当禁止体力最大的;

这个做法是错误的因为下一轮对方可能没有p操作,最后轮到自己来选的时候发现体力大的被选走了...

正确的做法应该是在b操作的时候进行决策,好在m不大决策结果可以用mask表示.

所有操作都应该是针对前m大,所以mask表示前m大里面能选的集合即可.

 #define rep(i,n) for(int i=0 ; i<(n) ; i++ )
#define ls ((rt)<<1)
#define rs (((rt)<<1)+1)
#define mid ((l+r)>>1)
#define maxn 110
#define mask (1<<20)
#define INF 1000000000
int dp[][mask];
int s[maxn],n,m;
struct node{
char c[];
int id;
};node o[maxn]; int dfs(int pos,int msk) { if (dp[pos][msk]!=INF) return dp[pos][msk];
if (pos==min(n,m)) return dp[pos][msk] = ; if (o[pos].id==) {
if (o[pos].c[]=='p') {
rep (i,m) {
if (!(msk&(<<i))) {
dp[pos][msk] = dfs(pos+,msk|(<<i)) + s[i];
break;
}
}
} else {
int tmp = -INF;
rep (i,m) {
if (!(msk&(<<i)))
tmp = max(tmp,dfs(pos+,msk|(<<i)));
}
dp[pos][msk] = tmp;
}
} else {
if (o[pos].c[]=='p') {
rep (i,m) {
if (!(msk&(<<i))) {
dp[pos][msk] = dfs(pos+,msk|(<<i)) - s[i];
break;
}
}
} else {
int tmp = INF;
rep (i,m) {
if (!(msk&(<<i))) {
tmp = min(tmp,dfs(pos+,msk|(<<i)));
}
}
dp[pos][msk] = tmp;
}
}
// printf("dp[%d][%d]=%d\n",pos,msk,dp[pos][msk]);
return dp[pos][msk];
} bool cmp(int x,int y) {return x>y;} int main(){
// freopen("test.txt","r",stdin);
scanf("%d",&n);
rep (i,n) scanf("%d",s+i);
scanf("%d",&m);
rep (i,m) {
scanf("%s%d",o[i].c,&o[i].id);
}
sort(s,s+n,cmp);
// rep (i,n) printf("%d ",s[i]); printf("\n");
rep (i,min(n,m)) rep (j,(<<m)) dp[i][j]=INF;
printf("%d\n",dfs(,));
return ;
}

D

问题可以描述为,求一个最大的集合所有元素满足:

  max{l0,l1..} <= v0,v1,v2... <= min{r0,r1}

因此,可以枚举l,它必定是某个3元组的l,并且是集合中最大的l,然后我们求出让答案最大的r:

  (1)  max{ (l,l) , (l,l+1) , (l,l+2) ... }

现在l已经确定,只考虑所有li<=l的3元组:

  (2)  每加入一个(li,vi,ri) , 它可以让 r属于[vi,ri] 的 询问增加1.

(1)是区间最值问题,(2)是成段更新问题,可以利用线段树实现.

接下来要考虑怎样枚举l,我们不希望每次枚举l都把3元组重新插入一次,这样复杂度太高.

可以先把所有3元组按l排序,按l单调增的顺序处理时,当考虑li的时候,之前插入的大部分还会被插入,部分应当被删除

现在考虑当l增加后,应当被删除的3元组:

  (3) 所有v < li 的3元组都要删除.

可以发现,被某3元组被删除后,就没有机会再被插入了,另外(3)可以用对于v的小顶堆维护.

 typedef long long llong;
#define rep(i,n) for(int i=0 ; i<(n) ; i++)
#define mid ((l+r)>>1)
#define ls ((rt<<1))
#define rs ((rt<<1)+1)
#define maxn 300010
int big[maxn<<],lz[maxn<<],n;
struct node{
int l,v,r,id;
bool operator < (const node& x) const {
return v>x.v;
}
};node worker[maxn/];
priority_queue<node> q; bool cmp(node x,node y) {
return x.l<y.l;
} void input() {
scanf("%d",&n);
rep (i,n) {
scanf("%d%d%d",&worker[i].l,&worker[i].v,&worker[i].r);
worker[i].id = i+;
}
sort(worker,worker+n,cmp);
} void pushdown(int rt) {
if (lz[rt]) {
big[ls] += lz[rt];
big[rs] += lz[rt];
lz[ls] += lz[rt];
lz[rs] += lz[rt];
lz[rt] = ;
}
} void pushup(int rt) {
big[rt] = max(big[ls],big[rs]);
} void add(int var,int L,int R,int l,int r,int rt) {
if (L<=l && r<=R) {
big[rt] += var;
lz[rt] += var;
return;
}
pushdown(rt);
if (L<=mid) add(var,L,R,l,mid,ls);
if (R>mid) add(var,L,R,mid+,r,rs);
pushup(rt);
} int query(int L,int R,int l,int r,int rt) {
int resl=,resr=;
if (L<=l && r<=R) return big[rt];
pushdown(rt);
if (L<=mid) resl = query(L,R,l,mid,ls);
if (R>mid) resr = query(L,R,mid+,r,rs);
pushup(rt);
return max(resl,resr);
} vector<int> rcd;
void solv() {
int ans=,lft;
rep (i,n) {
// printf("id:%d l:%d v:%d r:%d\n",worker[i].id,worker[i].l,worker[i].v,worker[i].r);
}
rep (i,n) {
while (!q.empty()) {
node tmp = q.top();
if (tmp.v<worker[i].l) {
// printf("del:\nid:%d l:%d v:%d r:%d\n",tmp.id,tmp.l,tmp.v,tmp.r);
add(-,tmp.v,tmp.r,,maxn-,);
q.pop();
} else break;
}
q.push(worker[i]);
// printf("add:\nid:%d l:%d v:%d r:%d\n",worker[i].id,worker[i].l,worker[i].v,worker[i].r);
add(,worker[i].v,worker[i].r,,maxn-,);
int tmp = query(worker[i].l,maxn-,,maxn-,);
// printf("cur ans: %d\n",tmp);
if (tmp>ans) {
ans = tmp;
lft = i;
}
} while (!q.empty()) {
node tmp = q.top(); q.pop();
add(-,tmp.v,tmp.r,,maxn-,);
}
// printf("\n\n");
// printf("lft:%d\n",lft);
rep (i,lft+) {
while (!q.empty()) {
node tmp = q.top();
if (tmp.v<worker[i].l) {
// printf("del:\nid:%d l:%d v:%d r:%d\n",tmp.id,tmp.l,tmp.v,tmp.r);
add(-,tmp.v,tmp.r,,maxn-,);
q.pop();
} else break;
}
q.push(worker[i]);
// printf("add:\nid:%d l:%d v:%d r:%d\n",worker[i].id,worker[i].l,worker[i].v,worker[i].r);
add(,worker[i].v,worker[i].r,,maxn-,);
}
for (int i=worker[lft].l ; i<maxn ; i++ ) {
int tmp = query(i,i,,maxn-,);
// printf("cur ans: %d\n",tmp);
if (tmp==ans) {
int L = worker[lft].l;
int R = i;
// printf("L:%d R:%d\n",L,R);
while (!q.empty()) {
node cur = q.top(); q.pop();
// printf("id:%d l:%d v:%d r:%d\n",cur.id,cur.l,cur.v,cur.r);
// printf("%d %d %d %d\n",L<=cur.v,cur.v<=R,cur.l<=L,cur.r>=R);
if (L<=cur.v && cur.v<=R && cur.l<=L && cur.r>=R) {
rcd.push_back(cur.id);
}
}
return;
}
}
} int main() {
// freopen("test","r",stdin);
input();
solv();
printf("%d\n",rcd.size());
rep (i,(int)rcd.size())
printf("%d%c",rcd[i],i==(int)rcd.size()-?'\n':' ');
return ;
}

E

简单地说就是用一堆斜率上升的线段来表示 函数f(x):x时间能获得的最大收益,答案就是f(s).

问题是如何维护表示f(x)的线段:

  (1) 维护它的凸性:

    明显不会成为最优决策的building可以最先排除(先满足v单调,再满足c也单调);

    每次买入新的building后,考虑它会比哪些更优,然后删除无用的;

    这里有很多单调性,总之就是某个building被删除之后,绝对不会再成为最优决策了,所以可以这样维护;

  (2) 对于新的building,找到最优决策;

因为(1)的作用,f(x)的形状是一个凸壳,做图可以发现,当新building与f(x)的交点t 小于某些顶点时, 该顶点以后的线段都可以删去,得到新的凸壳

对于(2),想象一条直线 y=build.c 与f(x)相交 , 那么最优决策一定是f(x)所有直线方程中,交点最靠前的一条,如果从第一条开始枚举的话,时间t是单调减的,直到找到最优决策.

又因为c保证了单调增,所以最优决策之前的线段都没有用了.

  (1),(2)可以用双端队列维护.

 #define rep(i,n) for(int i=0 ; i<(n) ; i++ )
#define ls ((rt)<<1)
#define rs (((rt)<<1)+1)
#define mid ((l+r)>>1)
#define maxn 2000200
#define INF 100000000000000000
llong s;
struct node{
llong c,v;
};node build[maxn];
struct line{
llong v,d,t,X,Y;
llong f(llong x) {
if( (x-t)>=(INF+d)/v+ ) return INF;
else return (x-t)*v+d;
}
llong x(llong y) {
return (y-d+v-)/v+t;
}
};line seg[maxn]; node use[maxn];
int n,tot,front,tail; bool cmp(node x,node y) {
return x.v<y.v;
} void input() {
scanf("%d%I64d",&n,&s);
build[] = (node){,};
rep (i,n) {
scanf("%I64d%I64d",&build[i+].v,&build[i+].c);
}
build[n+] = (node){s,INF};
} void pretreat() {
sort(build,build+n+,cmp);
rep (i,n+) {
if (i->= && build[i].v==build[i-].v) continue;
while (tot && use[tot-].c>=build[i].c) tot--;
use[tot++] = build[i];
}
} double findcross(line a,line b) {
double va,vb,ta,tb,da,db,x;
va = a.v; vb = b.v;
ta = a.t; tb = b.t;
da = a.d; db = b.d;
x = (va*ta-vb*tb+db-da)/(va-vb);
return x;
} llong solv() {
seg[tail++] = (line){,,,,};
rep (i,tot) {
while (tail-front> && seg[front].x(use[i].c) >= seg[front+].x(use[i].c)) front++;
llong t = seg[front].x(use[i].c);
if (i==tot-) return t;
line tmp = (line){use[i].v,seg[front].f(t)-use[i].c,t};
while (tail-front>) {
if (findcross(seg[tail-],seg[tail-])>findcross(seg[tail-],tmp)) tail--;
else break;
}
seg[tail++] = tmp;
}
return INF;
} int main(){
// freopen("test.txt","r",stdin);
input();
pretreat();
llong ans = solv();
printf("%I64d\n",ans);
return ;
}

  (未知的bug,E题代码wa 13了....)

Codeforce 222 div1的更多相关文章

  1. Codeforce 221 div1

    A 只要打个表就能发现,1,6,8,9的所有排列就可以产生0~6的余数了... 所以...走不下去的时候一定要打表... #define rep(i,n) for(int i=0 ; i<(n) ...

  2. Codeforce 219 div1

    B 4D"部分和"问题,相当于2D部分和的拓展,我是分解成2D部分和做的: f[x1][y1][x2][y2]=true/false 表示 左上(x1,y1) 右下(x2,y2)的 ...

  3. Codeforce 215 div1

    C 把每个qi看成点,则问题转化为:求一个最大的k,遍历k个点的完全图需要的最小步数+1不超过n, (这里+1的原因是把起点加进去) 讨论k的奇偶: k为奇数,每个点度数为偶数,这是一个欧拉回路,步数 ...

  4. ACM思维题训练 Section A

    题目地址: 选题为入门的Codeforce div2/div1的C题和D题. 题解: A:CF思维联系–CodeForces -214C (拓扑排序+思维+贪心) B:CF–思维练习-- CodeFo ...

  5. Codeforce Round #222 Div2

    这场断网,本来有个别人的比较卡的无线 但后面睡着了- -! C:额,逆向想下! B:... A:...

  6. Android Weekly Notes Issue #222

    Android Weekly Issue #222 September 11th, 2016 Android Weekly Issue #222 ARTICLES & TUTORIALS Fo ...

  7. PIC10F200/202/204/206/220/222/320/322芯片解密程序复制多少钱?

    PIC10F200/202/204/206/220/222/320/322芯片解密程序复制多少钱? PIC10F单片机芯片解密型号: PIC10F200解密 | PIC10F202解密 | PIC10 ...

  8. Acadia Lab 228 + Lab 222

    又是一对串烧实验,布好线后非常方便就可以一起完成. 连线方案一模一样: Lab 228 数码管骰子 核心代码如下: def loop() : global cnt global btn_read,se ...

  9. 微软Nokia 222:可拍照可上网 售价37美元 32GB的microSD卡扩展

    腾讯科技讯 8月27日,在几乎所有厂商都在智能手机领域大肆拼杀的时候,微软日前却悄悄地发布了一款功能手机Nokia 222. 目前,尽管全球许多发达国家的居民都对互联网已经再熟悉不过了,但事实上全球依 ...

随机推荐

  1. Java基础知识强化88:BigDecimal类之BigDecimal类引入和概述 以及 BigDecimal的使用(加减乘除)

    1. BigDecimal类概述: 由于在运算的时候,float类型和double很容易丢失精度.所以为了能够精确的表达.计算浮点数,Java提供了BigDecimal. BigDecimal:不可变 ...

  2. Citrix Presentation server can not contact the license server

    If you come across the above error,  you may also come across one or more of the errors below within ...

  3. ShareSDK.xml 配置

    简要说明 <ShareSDK AppKey="1089fa233237e" /> <!-- 修改成你在sharesdk后台注册的应用的appkey" - ...

  4. C# 面向对象 , 类与对象

    一,类的字段 类的字段, 就是类里面的 数据. 二,类的方法 1 , 函数的重载 , 如何判断:  是由两个或多个同名函数组成的,但是函数要有不同的参数.或个数.(参数  是函数的输入的东西) shu ...

  5. ASP.NET-FineUI开发实践-9(二)

    其实我也不会,老实教人学怕误人子弟,但是抱着毁人不倦的精神还是糊弄糊弄个别小白吧,最起码能加点原创. 下面以表单为例,打开官方项目,版本为FineUI_4.1.1,打开form_compare页,右键 ...

  6. 关于ASP.Net 4.0的ClientID

    我们知道因为在原来的ASP.NET应用程序中使用服务端控件在生成ClientID的时,是很难控制的,特别是在嵌套的控件的时候,比如在多个嵌套Repeater中要控制某一个控件生成的html的ID属性, ...

  7. linux入门。删除不用到内核,为boot分区释放空间

    在终端中输入如下命令: dpkg --get-selections|grep linux-image 这时会列出系统中所有到内核. 你可以使用 uname -a 查看你当前使用到内核. 然后用 sud ...

  8. MySQL查询优化处理

    查询的生命周期的下一步是将一个sql转化成一个执行计划,MySQL再依照这个执行计划和存储引擎进行交互.这包括多个子阶段:解析sql,预处理,优化sql执行计划.这个过程中任何错误(例如语法错误)都可 ...

  9. 《Effective C++》 阅读小结 (笔记)

    A person who is virtuous is also courteous. "有德者必知礼" 书本介绍:<Effective C++:改善程序与设计的55个具体做 ...

  10. nginx重定向规则详细介绍

    为何要使用301重定向 在网站建设中需要网页重定向的情况很多:如网页目录结构变动,网页重命名.网页的扩展名改变.网站域名改变等.如果不做重定向,用户的收藏和搜索引擎数据库中的旧地址只能让访客得到一个4 ...