cf 之lis+贪心+思维+并查集
https://codeforces.com/contest/1257/problem/E
题意:有三个集合集合里面的数字可以随意变换位置,不同集合的数字,如从第一个A集合取一个数字到B集合那操作数+1,求最少操作次数使得一二三集合连起来是升序
我们先把每个集合排个序,然后拼起来,求一遍lis(最长升序序列:https://blog.csdn.net/Joined/article/details/78058362),然后n-len即为答案
网上那些什么dp什么的。。嘻嘻太高深
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+7;
int k1,k2,k3,n,a[maxn],dp[maxn],b[maxn],len;
void lis(){
len=1;b[1]=a[0];
for(int i=1;i<n;i++){
b[len+1]=n+1;
int l=0,r=len+1,ans=0;
while(l<=r){
int mid=(l+r)/2;
if(a[i]<b[mid]){
ans=mid;
r=mid-1;
}else{
l=mid+1;
}
}
b[ans]=a[i];
if(ans>len)len++;
}
cout<<n-len<<endl;
}
int main(){
scanf("%d%d%d",&k1,&k2,&k3);
n=k1+k2+k3;
for(int i=0;i<k1+k2+k3;i++){
scanf("%d",&a[i]);
}
sort(a,a+k1);
sort(a+k1,a+k1+k2);
sort(a+k1+k2,a+k1+k2+k3);
lis();
}
https://codeforces.com/contest/1257/problem/D
赛后才想出来的一道题:
题意大概是,给你一串怪物,怪物有攻击力,你有几个英雄,每个英雄有攻击力和耐力,攻击力大于怪物的你就击败一个怪物,但耐力减少一,改天结束的条件是,改英雄耐力耗完或者是改英雄攻击力打不过怪物无奈退出,每天可安排一个英雄,英雄可多次使用,至少多少天清楚掉这些怪物。
很容易想到二分找到一个大于该怪物的的英雄,贪心选取最优的一个。
思路:我们先排序
bool cmp(node A,node B)
{
if (A.pi!=B.pi) return A.pi<B.pi;
return A.si>B.si;
}
排完序后,我们要对这些英雄进行优化,为什么要优化呢?因为我们的思路为以下
遇到第一个怪物然后二分查找第一个英雄,然后枚举怪物,英雄遇到一个打不过的,我们往后找,英雄战力是递增的嘛,而且我们希望找到的是英雄更大耐力也更大的吧,你肯定不想找耐力反而更少的,浪费时间。
所以我们的优化有两个,第一出现相同战力的英雄经过我们上面的排序,剔除掉耐力低的。
第二,由于我们的战力从小排到大,出现战力大于a,耐力又大于a的b,肯定是直接舍弃a,经过这些筛选,就减少了不必要的枚举。
接下来就可以开始枚举了
#include <bits/stdc++.h>
#define N 200010
using namespace std;
int res,len,now,now2,tmp,maxx;
int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
return x*f;
}
struct node{
int pi,si;
}b[N];
int n,tmpp;
int a[N];
bool cmp(node A,node B)
{
if (A.pi!=B.pi) return A.pi<B.pi;
return A.si>B.si;
}
inline erfen(int x)
{
int l=1,r=tmpp,mid,t;
while (l<=r){
mid=(l+r)/2;
if (x==b[mid].pi) return mid;
if (x<b[mid].pi) r=mid-1,t=mid;
else l=mid+1;
}
return t;
}
int main()
{
// freopen("t.txt","r",stdin);
int m;
int T=read();
while (T--)
{
n=read();
maxx=0;
for(int i=1;i<=n;i++)
a[i]=read(),maxx=max(maxx,a[i]);
m=read();
for (int i=1;i<=m;i++)
b[i].pi=read(),b[i].si=read();
sort(b+1,b+m+1,cmp);
if(maxx>b[m].pi){
puts("-1");
continue;
}
tmp=1;
for(int i=2;i<=m;i++)
if(b[i].pi!=b[tmp].pi) b[++tmp]=b[i];
tmpp=tmp;
tmp=1;
for(int i=2;i<=tmpp;i++)
{
while (b[i].si>=b[tmp].si&&tmp>0) tmp--;
b[++tmp]=b[i];
}
tmpp=tmp;
res=0;
now=1;
while(now<=n)
{
len=1;
now2=erfen(a[now]);
while(b[now2].si>len){
if(a[now+1]<=b[now2].pi) now++,len++;
else{
while(a[now+1]>b[now2].pi&&len+1<=b[now2].si) now2++;
if(a[now+1]>b[now2].pi||len+1>b[now2].si) break;
now++;len++;
}
}
res++;
now++;
}
printf("%d\n",res);
}
}
Ivan the Fool and the Probability Theory
给定一个N×M N\times MN×M的方格图,现在要求将每个格子染成黑白两种颜色,要求每个格子至多和四个方向(上下左右)上的一个格子同色,问有多少种方案。
我们先以一维的时候来解释,一个格子的时候有两种情况,二个格子有四个情况,那么三个格子的时候就是要么第一格+两个相同颜色的,要么就是第二个格子加上一个格子,所以
就是f1+f2斐波那契就出来了
接下来思考二维,实际上例如第一行任意一个地方连续出现两个方块(称之为行连续),那么第二行必须与上面状态相反,以此类推,第一行确定了,下面的颜色随之确定了,(那还有第一行不出现连续的呢? 那就是间隔的,那无非就是黑白黑白 然后白黑白黑两种,他下一行必定和他相反,这就和上面同个道理(。。。相同不行? 嗯肯定是可以的,但是相同了,不就出现相邻有相同颜色了嘛,(我们自己称之为列连续)那不就和上面的行连续一个道理吗,所以第一列状态确定,后面的也随之确定
那不就好起来了,f【n】+f【m】-2,-2是因为行的时候我们计算了黑白黑白 白黑白黑,列的时候依然会计算到这个,重复了两个。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=1E5+7;
ll dp[N];
int main(){
int n,m;
scanf("%d%d",&n,&m);
int x=max(n,m);
dp[1]=2;
dp[2]=4;
for(int i=3;i<=x;i++) dp[i]=(dp[i-1]+dp[i-2])%mod;
ll sum=(dp[n]+dp[m]-2+mod)%mod;
cout<<sum<<endl;
return 0;
}
https://codeforces.com/contest/1253/problem/D
看代码很容易理解,并查集
#include <bits/stdc++.h> using namespace std; set<int> s[200010]; int n,m,x,y;
int p[200010];
int mx[200010];
int mn[200010];
long long sum[200010]; void init() {
for (int i = 0; i < 200010; ++i) {
p[i] = i;
mn[i] = 1e9;
}
} int find (int x) {
return x == p[x] ? p[x] : p[x] = find(p[x]);
}
void merge(int x, int y) {
p[find(y)] = find(x);
}
int main() {
init();
scanf("%d %d", &n, &m);
for (int i = 0; i < m; ++i) {
scanf("%d %d", &x, &y);
if(find(x) != find(y))
merge(x,y);
}
set<int> parents;
for (int i = 1; i <= n; ++i) {
parents.insert(find(i));
mx[find(i)] = max(mx[find(i)], i);
mn[find(i)] = min(mn[find(i)], i);
}
vector<pair<int, int> > r;
for(set<int>::iterator it=parents.begin();it!=parents.end();it++)
r.push_back({mn[*it], mx[*it]}); sort(r.begin(), r.end());
priority_queue<int > q;
int ans = 0;
for(int i = 0; i < (int) r.size(); ++i) {
while(!q.empty() and q.top()< r[i].first) q.pop();
if(!q.empty()) ans++;
q.push(r[i].second);
} cout << ans << "\n";
return 0;
}
D1/D2. The World Is Just a Programming Task
题目大意
有一个括号序列,你需要选择两个位置,将位置上的括号交换,使得最后得到的循环位置最大。
循环位置的定义是:我们将前i ii个位置上的字符串取出放到字符串的最后,所得的括号序列是一个合法的序列。
分析
首先我们可以知道,对于一个左右括号数量并不相同的序列,它没有循环位置。我们将这种情况直接特判掉。
我们先将整个括号序列转成一个合法的序列。容易证明这是一定存在的。
那么这个序列的循环节位置就长这样,就是一级括号所在的位置:
那么我们可以发现,对于若我们交换一个一级括号的左右括号,如下:
若我们记翻转的一级括号内的二级括号数量为a aa,那么这个操作的答案就是a+1 a+1a+1。
我们也可以交换一个二级括号的左右括号。
简单画图可以知道答案是一级括号的数量+翻转的二级括号内的数量+1。
通过画图可以发现翻转一个三级括号是没用的。
O(N) O(N)O(N)扫一遍就行了。
#include <cstdio>
#include <algorithm>
using namespace std; const int Maxn = 300000; int N, s[Maxn * 2 + 5];
char str[Maxn + 5];
int lef[Maxn * 2 + 5], dep[Maxn * 2 + 5];
int siz[Maxn * 2 + 5], sum[Maxn * 2 + 5];
int stk[Maxn + 5], tp; int main() {
#ifdef LOACL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d %s", &N, str + 1);
int cnt = 0;
for(int i = 1; i <= N; i++) {
s[i] = s[i + N] = (str[i] == '(' ? 1 : -1);
cnt += s[i];
}
if(cnt) {
puts("0\n1 1");
return 0;
}
int pos = 1;
for(int i = 1; i <= N * 2; i++) {
cnt += s[i];
if(cnt < 0) pos = i + 1, cnt = 0;
else if(i - pos + 1 >= N) break;
}
for(int i = pos; i <= pos + N - 1; i++)
if(s[i] == -1) {
int p = stk[tp--];
sum[tp]++, lef[i] = p, dep[i] = tp;
siz[i] = sum[tp + 1], sum[tp + 1] = 0;
} else stk[++tp] = i;
int ans = sum[0], l = 1, r = 1;
for(int i = pos; i <= pos + N - 1; i++) {
if(dep[i] == 0)
if(siz[i] + 1 > ans)
ans = siz[i] + 1, l = lef[i], r = i;
if(dep[i] == 1)
if(sum[0] + siz[i] + 1 > ans)
ans = sum[0] + siz[i] + 1, l = lef[i], r = i;
}
printf("%d\n%d %d\n", ans, (l - 1) % N + 1, (r - 1) % N + 1);
return 0;
}
https://codeforces.com/contest/1253/problem/C
给个序列,给个m,每一天最多选m个,第k天地贡献是所选地m个元素和乘以k。
问1到n天,每天的方案,不可选重复,最小地贡献是多少,如果简单地贪心暴力算,就会超时,
在纸上画一下过程,当大于m天时,就是讲后来的序列放到第一天,第一天有一个到第二天,第二天有一个到第三天,所以每个数字是有以m为周期的贡献的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx=200005;
ll a[maxx],b[maxx];
int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
return x*f;
}
int main()
{
// freopen("t.txt","r",stdin); int n=read(),m=read();
ll ans=0;
for(int i=1;i<=n;i++) a[i]=read();
sort(a+1,a+1+n);
for(int i=1;i<=n;i++){
b[i]=a[i];
ans+=a[i];
if(i-m>0) b[i]+=b[i-m],ans+=b[i-m];
if(i==n) printf("%lld\n",ans);
else printf("%lld ",ans);
}
return 0;
}
https://codeforces.com/contest/1253/problem/B
简单说一个整数k表示k进去,而-k表示他出来,每个整数一天只可能进去一次,所以出现1 -1 1 -1,说明这不是同一天的,同样,当整个办公室没人了,说明这一天已经过了。
所以模拟一下过程
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef double ld;
typedef vector<ll> vi;
#define F(a) for ( ll i = 0; i < (ll)(a); ++i )
ll read(){
char c=getchar();ll x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
return x*f;
}
int main() {
// freopen("t.txt","r",stdin);
ll n;n=read();
vi a(n);for(int i=0;i<n;i++)a[i]=read();
map<ll,ll> m;
set<ll> visit;
vi res;
ll len=0,cnt=0;
bool ok=1;
for(int i=0;i<n;i++){
ll c=a[i];
++len;
if(c>0){ // enter
if(visit.count(c)) ok=0;
if(m[c]++ == 0){
visit.insert(c);
cnt++;
}
}else{ // leave
c=-c;
--m[c];
if(m[c] == 0){
if(--cnt == 0){
visit.clear();
res.push_back(len);
len=0;
}
}else if(m[c]<0){
ok=0;
}
}
}
if(cnt)ok=0;
if(ok){
cout<<res.size()<<endl;
for(int i=0;i<res.size();i++)cout<<res[i]<<' ';
cout<<endl;
}else{
cout<<-1<<endl;
}
https://codeforces.com/contest/1239/problem/C
题意:每个人按座位号从左到右做好,每个人有一个t值,到达t时刻就会开始渴,每个人喝水的时间是p,然后就是一个人渴了如果他前面的座位有人已经在排队或者正在接水,那么他就不会去排队,反之则排。事实上就是模拟
搞一个优先队列维护一下事件结构体:时间,人的编号,入队还是出队
再维护两个 setset ,队列内的人 inQueueinQueue ,想要进入队列内的人 wantwant
然后模拟模拟模拟!
初始把所有入队事件塞到优先队列,顺便维护一下当前最后一个取完水的时刻
每次取出优先队列里面时间最小的,时间相同优先取入队的,同时间都入队优先取编号小的
然后如果是入队,那么看看当前队列内编号最小的比较一下编号,然后根据比较结果看看是直接入队还是先塞到 wantwant 里面
如果是出队,直接更新一下答案和队列,然后看看 wantwant 里面有没有人能入队
然后就做完了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
using namespace std;
typedef long long ll;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
const int N=2e5+7;
int n,P;
ll ans[N];
struct dat {
ll tim; int id; bool flag;
dat (ll _tim=0,int _id=0,bool _flag=0) { tim=_tim,id=_id,flag=_flag; }
inline bool operator < (const dat &tmp) const {
if(tim!=tmp.tim) return tim>tmp.tim;
return flag!=tmp.flag ? flag>tmp.flag : id>tmp.id;
}
}D[N];
priority_queue <dat> Q;
set <int> inQ,wnt;
int main()
{
n=read(),P=read();
for(int i=1;i<=n;i++) D[i]=dat(read(),i,0);
for(int i=1;i<=n;i++) Q.push(D[i]);
ll las=0;
while(!Q.empty())
{
dat t=Q.top(); Q.pop();
if(!t.flag)//要入队地的
{
if(inQ.empty()||*inQ.begin()>t.id)//对物说空或最小的座位号都大于这个要入对的,则可以进来
{
las=max(las,t.tim);//不要多想就是取最大的,因为上一次在喝水的时候你是不能排队的
Q.push(dat(las+P,t.id,1));//然后把要出队的时间弄好,加个出队标志“1”,然后放到优先队列哩,按所说的规则进行排序 inQ.insert(t.id); //队伍更新一人
las+=P;
}
else wnt.insert(t.id);//如果是无法排队,放进想进队伍的set里
continue;
}
ans[t.id]=t.tim; inQ.erase(t.id);//此时是出队的更新这个出队ide,队列里删除这个人
if(!wnt.empty() && (inQ.empty()||*inQ.begin()>*wnt.begin()))//出队的更新完,然后我们就有空来看看想进队里的
{
auto p=wnt.begin(); inQ.insert(*p);
Q.push(dat(las+P,*p,1)); las+=P; wnt.erase(p);
}
}
for(int i=1;i<=n;i++) printf("%lld ",ans[i]); puts("");
return 0;
}
cf 之lis+贪心+思维+并查集的更多相关文章
- Luogu 1525 【NOIP2010】关押罪犯 (贪心,并查集)
Luogu 1525 [NOIP2010]关押罪犯 (贪心,并查集) Description S城现有两座监狱,一共关押着N名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨 ...
- hdu 2480 贪心+简单并查集
Steal the Treasure Time Limit: 10000/6000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Othe ...
- POJ-1456 Supermarket(贪心,并查集优化)
Supermarket Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 10725 Accepted: 4688 Descript ...
- 1202. [HNOI2005]狡猾的商人【贪心 或 并查集】
Description 刁姹接到一个任务,为税务部门调查一位商人的账本,看看账本是不是伪造的.账本上记录了n个月以来的收入情况,其中第i 个月的收入额为Ai(i=1,2,3...n-1,n), .当 ...
- Codeforces#514E(贪心,并查集)
#include<bits/stdc++.h>using namespace std;long long w[100007],sum[100007];int fa[100007],degr ...
- [POJ1456]Supermarket(贪心 + 优先队列 || 并查集)
传送门 1.贪心 + 优先队列 按照时间排序从前往后 很简单不多说 ——代码 #include <queue> #include <cstdio> #include <i ...
- 洛谷 P1525 关押罪犯 (贪心,扩展域并查集)
题意:有\(n\)个罪犯,\(m\)对罪犯之间有仇,现在将这些罪犯分到两个监狱里去,问两个监狱里有仇罪犯之间的最大权值最小为多少. 题解:先按边权从大到小排序,然后贪心,边权大的两个罪犯,我们一定要先 ...
- 思维+并查集 hdu5652
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5652 题意: 输入T,接下来T个样例,每个样例输入n,m代表图的大小,接下来n行,每行m个数,代表图, ...
- Gym - 101243F Vitamins(思维+并查集)
题意 有三种药丸,白色W>红色R>蓝色B,给你m个约束条件,问你n个药丸的颜色,不能确定颜色输出‘?’ 题解 如果1<2<3,只要找到2就能确定1和3的颜色 如果2=4,只要确 ...
随机推荐
- mysql 排它锁之行锁、间隙锁、后码锁
MySQL InnoDB支持三种行锁定 行锁(Record Lock):锁直接加在索引记录上面,锁住的是key. 间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙不变.间隙锁是针对事务 ...
- Java 基础复习 基础数据类型与包装器类型
Java 基础 基础数据类型与包装器类型 基础数据类型 java 中包含哪些基础数据类型,默认值分别是多少? 基础数据类型 byte short int long double float char ...
- DOMContentLoaded vs jQuery.ready vs onload, How To Decide When Your Code Should Run
At a Glance Script tags have access to any element which appears before them in the HTML. jQuery.rea ...
- JT/T 808-2013 道路运输车辆卫星定位系统北斗兼容车载终端通讯协议技术规范
文档下载地址:JT/T 808-2013 道路运输车辆卫星定位系统北斗兼容车载终端通讯协议技术规范
- SpringBoot中使用Zuul
Zuul提供了服务网关的功能,可以实现负载均衡.反向代理.动态路由.请求转发等功能.Zuul大部分功能是通过过滤器实现的,除了标准的四种过滤器类型,还支持自定义过滤器. 使用@EnableZuulPr ...
- 【好书推荐】《剑指Offer》之硬技能(编程题12~16)
本文例子完整源码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/sword <[好书推荐]& ...
- 为什么 netstat 对某些服务只显示了 tcp6 监听端口
最近偶尔发现一个比较奇怪的现象,netstat 查看监听的服务端口时,却只显示了 tcp6 的监控, 但是服务明明是可以通过 tcp4 的 ipv4 地址访问的,那为什么没有显示 tcp4 的监听呢? ...
- 浅谈——MySQL存储引擎
博文大纲: 一.MyISAM存储引擎: 二.InnoDB存储引擎: 三.Memory存储引擎特点: 四.如何选择合适的存储引擎? 前言 数据库存储引擎是数据库底层软件组件,数据库管理系统(DBMS)使 ...
- 《Web Development with Go》Mangodb插入struct数据
学习数据持久化. package main import ( "fmt" "log" "time" "gopkg.in/mgo.v ...
- 给OPi Zero Plus添加USB启动功能
为使OPi Zero Plus支持U盘启动,需要在板载的SPI Flash当中刷入uboot.在这个过程当中绕了很多弯路,特此记录 最终操作步骤见文末 网上的教程仅使用sudo modprobe sp ...