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. shell 脚本实现的守护进程

    转自:http://blog.csdn.net/cybertan/article/details/3235722 转自:http://blog.sina.com.cn/s/blog_4c451e0e0 ...

  2. 编译lua版本问题

    Compile++ thumb  : game_shared <= main.cppjni/hellocpp/main.cpp: In function 'void Java_org_cocos ...

  3. Activity启动机制

    以下资料摘录整理自老罗的Android之旅博客,是对老罗的博客关于Android底层原理的一个抽象的知识概括总结(如有错误欢迎指出)(侵删):http://blog.csdn.net/luosheng ...

  4. 通过编写一个简单的漏洞扫描程序学习Python基本语句

    今天开始读<Python绝技:运用Python成为顶级黑客>一书,第一章用一个小例子来讲解Python的基本语法和语句.主要学习的内容有:1. 安装第三方库.2. 变量.字符串.列表.词典 ...

  5. codevs 4650 破损的键盘(链表)

    /* 之前一直不重视链表 (好吧说实话主要是看着板子都是指针就怂了T.T) 这道题比较基础 应用了链表的思想 数组模拟指针 遇到的问题就是跑着跑着光标跳到前面或者跳到后面 我们用next储存每个点下一 ...

  6. ASP.NET-FineUI开发实践-9(四)

    现在是这么个问题,在开发中表格是动态出来的,就是标准板是全部字段列出,客户要根据情况列出自己想要的,在增加操作页面的同时要是能用前台自带的功能直接保存到后台就好了,现在的列显示和隐藏是不回发的. 1. ...

  7. mysql 存储引擎MYSIAM和INNODB特性比较

    事物:MYISAM不支持事物,MyISAM类型的表强调的是性能,其执行数度比InnoDB 类型更快.如果不考虑事物,大量的select和insert适合MYISAM表 锁:MYISAM支持表锁    ...

  8. Domino 8.5 WebService开发一例

    原文地址:Domino 8.5 WebService开发一例作者:bj木棉 需求是要调用一个Domino上的WebService/JAVA来实现与人事管理系统里的人员同步,就是在人事管理系统中增加用户 ...

  9. Java反射 - 3(动态代理)

    动态代理是对包装模式的升级,可以动态的传入需要代理的对象实现代理 准备如下 1. 被代理类的接口 2.被代理类 3.处理器:InvocationHandler 4.代理调用:Proxy.newInst ...

  10. 【C++学习之路】派生类的构造函数(二)

    二.有内嵌对象的派生类 1.一般来说,我们会这样定义构造函数 student( int i, string nam, int pid, string pnam, int sid) : person( ...