老头马桶枪!
Time Limit: 1000 MS Memory Limit: 64 MB
众所周知,集训队的题目是非常困难的。因此,队员们在挂机AK之后,常常会玩一些游戏。
这次,率先AK的周大爷想出了一个叫老头马桶枪的游戏。
在一个小岛上,有三个物种,一共N个生物生活在一起,他们分别是老头、马桶和枪。他们之间的关系是相互克制的,就像包剪锤一样。老头克制马桶,马桶克制枪,枪又克制老头。
现在,集训队的三名成员,小周,小钱和小胡按次序(周、钱、胡)轮流给出信息,信息有两种形式:
第一种记录方式是1 X Y
是同类。
第二种记录方式是2 X Y
,表示 X对 Y有攻击性行为( X克制 Y)。
当其中一人给出的信息和之前的人给出的信息矛盾时,他便输了,要请吃晚饭。那么,聪明的你能帮助他们看出谁会输呢?(最多只会有一个人输,当多条信息矛盾时,最先给出矛盾信息的人输)
Input
第一行包含两个整数N
Output
输出一行,表示最先给出矛盾信息的人是谁。若是小周,输出1
;若是小钱,输出2
;若是小胡,输出3
;若没有人给出矛盾信息,输出-1
。
Sample input and output
Sample Input | Sample Output |
---|---|
100 7 1 100 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5 | 1 |
Source
UESTC Online Judge
Copyright (C) 2012 - 2018 Ruins He(@ruinshe), Jianjin Fan(@pfctgeorge) and Yun Li(@mzry1992). Project home
Any Problem, Please Report On Issues Page.
这一题仿照poj1182的食物链https://blog.youkuaiyun.com/keepcoral/article/details/79946060
食物链循环,a->b,b->c,c->a,这里学到一种新的方法,上面写的实在有点难懂,特别是推到关系那里,真的很难想,这里从别人博客上学到了新方法。
首先,有n个动物,那么我们开辟3n个集合,x,x+n,x+2n,分别表示与x同类,被x吃的,吃x的集合,所以两个动物x和y下面有两种情况:
1 x和y是同类,那么和y同类的即等价于和x同类,吃y的等价于吃x的,被y吃的等价于被x吃的,所以这三个集合要同时合并,
Union(x,y);
Union(x+n,y+n);
Union(x+2*n,y+2*n)
判断真假条件:x被y吃||x吃y,那么直接表示就是x在y+n的集合||x在y+2n的集合,这里y+n代表被y吃的集合,y+2n代表吃y的集合,所以判断条件为judge(x,y+n)&&judge(x,y+2n),
2 x吃y,那么被y吃的等价于吃x的,和y同类的等价于被x吃的,吃y的等价于x的同类,所以集合为
Union(x,y+n);
Union(x+n,y+2*n);
Union(x+2*n,y);
判断真假条件:x和y同类||y吃x,所以条件为 judge(x,y)&&judge(x,y+n)(我的代码可能会有点出入,因为n和2*n我写的恰好相反的)
这里的合并是最关键的操作,下面的几道题也是一样,也就是当不知道情况的时候才可以去合并,这是题目的要求
#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int father[300009];
int Find(int x)
{
if(x!=father[x]) father[x]=Find(father[x]);
return father[x];
}
int Union(int x,int y)
{
int i=Find(x);
int j=Find(y);
if(i!=j) father[i]=j;
}
int judge(int x,int y)
{
int i=Find(x);
int j=Find(y);
if(i!=j)//不同类
{
return 1;
}
else return 0;
}
int main()
{
int n,m,i,j,k;
scanf("%d%d",&n,&m);
for(i=1;i<=3*n;i++) father[i]=i;
for(i=1; i<=m; i++)
{
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if(op==1)//x和y是同类,所以判断不同类
{
if(judge(x,y+n)&&judge(x,y+2*n))
{
Union(x,y);
Union(x+n,y+n);
Union(x+2*n,y+2*n);
}
else
{
if(i%3!=0)printf("%d\n",i%3);
else printf("3\n");
break;
}
}
else//x吃y,要判断同类或者y吃x
{
if(judge(x,y)&&judge(x,y+2*n))
{
Union(x,y+n);
Union(x+n,y+2*n);
Union(x+2*n,y);
}
else
{
if(i%3!=0)printf("%d\n",i%3);
else printf("3\n");
break;
}
}
}
if(i==m+1) printf("-1\n");
return 0;
}
I - 不如把并查集加上个计数功能吧
Time Limit: 1000 MS Memory Limit: 64 MB
在另一个宇宙,一个月有 N
天一样。
但民众并不满足于此,他们想知道有多少天的天气和第 X
天一样。现在,作为一个聪明的程序员,你能帮他们解决这个问题吗?
Input
第一行包含两个整数N
Output
对于每一个询问,输出一行,包含一个整数,表示和z
天气一样的天数。
Sample input and output
Sample Input | Sample Output |
---|---|
10 5 1 2 2 3 3 4 1 5 6 7 2 1 6 | 5 2 |
UESTC Online Judge
Copyright (C) 2012 - 2018 Ruins He(@ruinshe), Jianjin Fan(@pfctgeorge) and Yun Li(@mzry1992). Project home
Any Problem, Please Report On Issues Page.
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef unsigned long long ll;
#define N 100009
#define M 400090
#define C 18446744073709551611
int father[N];
int sum[N];
int n,m;
int Find(int x)
{
if(father[x]!=x) father[x]=Find(father[x]);
return father[x];
}
void Union(int x,int y)
{
int i=Find(x);
int j=Find(y);
if(i!=j)
{
father[i]=j;
sum[j]+=sum[i];
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
father[i]=i;
sum[i]=1;
}
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
Union(x,y);
}
for(int i=1;i<=n;i++) Find(i);
scanf("%d",&m);
while(m--)
{
int x;
scanf("%d",&x);
printf("%d\n",sum[father[x]]);
}
}
K - 爱吃瓜的伊卡洛斯(1)
Time Limit: 1000 MS Memory Limit: 64 MB
伊卡洛斯很爱吃西瓜。一次,他来到一个西瓜摊旁,发现水果摊有N个西瓜,但只有红色和黄色两种颜色。
伊卡洛斯很想知道知道一些信息,便于老板交谈了起来。
当老板的话的第一个字符为”A”时,老板会告诉伊卡洛斯一些信息,格式如下:
A x y 1 这句话表示第x个西瓜和第y个西瓜是同一种颜色的。
A x y 2 这句话表示第x个西瓜和第y个西瓜是不同种颜色的。
当然,为了考验伊卡洛斯有没有认真听, 老板也会时不时问伊卡洛斯一些问题,格式如下:
Q x y 这句话表示询问第x个西瓜和第y个西瓜是不是同一种颜色,如果确定为同一种颜色,伊卡洛斯需要回答1
;确定为不同种颜色,伊卡洛斯需要回答2
;无法确定时伊卡洛斯回答3
。
注意,伊卡洛斯是根据已获得的信息来回答的。也就是只有这个问题之前的信息才为已知信息。
老板说,只有回答对他全部的问题,伊卡洛斯才能吃到瓜,他聪明的想到了让你来帮助他。
Input
第一行包含两个整数N和M,N是西瓜总数,M是以A或Q开头的老板的话总和。
以下M行,每行包含一条老板的话。形式有A x y 1或A x y 2或Q x y。1≤N≤100000 1≤M≤200000 1≤X,Y≤N数据保证没有矛盾
Output
对于每一条$Q$指令,输出1
/2
/3
代表两个西瓜颜色的关系。
Sample input and output
Sample Input | Sample Output |
---|---|
6 9 A 1 2 1 A 1 3 1 A 1 4 2 Q 2 4 Q 1 6 A 3 6 1 A 4 5 2 Q 1 5 Q 1 6 | 2 3 1 1 |
Hint
西瓜的颜色有且仅有两种!
对立面的并查集,x表示同色的,x+n表示和x不同颜色的,所以如果说了不同颜色,那么Union(x,y),Union(x+n,y+n) 如果不同颜色,那么Union(x,y+n),Union(x+n,y),这里简单来说就是x和y不同颜色,那么x属于和y不同颜色的集合(y+n)这里。查询的时候,如果Find(x)==Find(y),同色;如果Find(x)=Find(y+n)||Find(y)=Find(x+n),不同色;其它不清楚
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef unsigned long long ll;
#define N 100009
#define M 400090
#define C 18446744073709551611
int father[5*N];
int sum[N];
int n,m;
int Find(int x)
{
if(father[x]!=x) father[x]=Find(father[x]);
return father[x];
}
void Union(int x,int y)
{
int i=Find(x);
int j=Find(y);
if(i!=j)
{
father[i]=j;
}
}
int main()
{
char s[10];
scanf("%d%d",&n,&m);
for(int i=1; i<=2*n; i++)
{
father[i]=i;
}
while(m--)
{
int x,y,flag;
scanf("%s%d%d",&s,&x,&y);
if(s[0]=='A')
{
scanf("%d",&flag);
if(flag==1)
{
Union(x,y);
Union(x+n,y+n);
}
else if(flag==2)
{
Union(x,y+n);
Union(y,x+n);
}
}
else
{
if(Find(x)==Find(y)) printf("1\n");
else if(Find(x)==Find(y+n)||Find(y)==Find(x+n)) printf("2\n");
else printf("3\n");
}
}
}
L - 爱吃瓜的伊卡洛斯(2)
Time Limit: 1000 MS Memory Limit: 64 MB
伊卡洛斯很爱吃西瓜。一次,他来到一个西瓜摊旁,发现水果摊有$N$个西瓜,西瓜有红色、黄色、绿色、蓝色……等等数不清的颜色。伊卡洛斯很想知道知道一些信息,便于老板交谈了起来。当老板的话的第一个字符为”A
”时,老板会告诉伊卡洛斯一些信息,格式如下:$A\ x\ y\ 1$ 这句话表示第$x$个西瓜和第$y$个西瓜是同一种颜色的。$A\ x\ y\ 2$ 这句话表示第$x$个西瓜和第$y$个西瓜是不同种颜色的。
当然,为了考验伊卡洛斯有没有认真听, 老板也会时不时问伊卡洛斯一些问题,格式如下:$Q\ x\ y$ 这句话表示询问第$x$个西瓜和第$y$个西瓜是不是同一种颜色,如果确定为同一种颜色,伊卡洛斯需要回答1
;确定为不同种颜色,伊卡洛斯需要回答2
;无法确定时伊卡洛斯回答3
。注意,伊卡洛斯是根据已获得的信息来回答的。也就是只有这个问题之前的信息才为已知信息。
老板说,只有回答对他全部的问题,伊卡洛斯才能吃到瓜,他聪明的想到了让你来帮助他。
Input
第一行包含两个整数N
以下 M行,每行包含一条老板的话。形式有 A x y 1或 A x y 2或 Q x y。 1≤N≤100000 1≤M≤200000 1≤X,Y≤N
数据保证没有矛盾
Output
对于每一条Q
指令,输出1
/2
/3
代表两个西瓜颜色的关系。
Sample input and output
Sample Input | Sample Output |
---|---|
6 9 A 1 2 1 A 1 3 1 A 1 4 2 Q 2 4 Q 1 6 A 3 6 1 A 4 5 2 Q 1 5 Q 1 6 | 2 3 3 1 |
Hint
西瓜的颜色可以有无数多种!
#include <iostream>
#include <cstdio>
#include <string>
#include <set>
using namespace std;
int father[100009];
set<int>s[100009];//代表s[x]代表和x不同的元素的集合
int Find(int x)
{
if(x!=father[x]) father[x]=Find(father[x]);
return father[x];
}
void Union(int i,int j)
{
int x=Find(i);
int y=Find(j);
if(x!=y)
{
if(s[x].size()>s[y].size()) swap(s[x],s[y]);//小的合并到大的
father[x]=y;
for(set<int>::iterator it=s[x].begin(); it!=s[x].end(); it++)
{
s[y].insert(Find(*it));//找到根节点
}
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
father[i]=i;
}
while(m--)
{
char c[10];
scanf("%s",c);
if(c[0]=='A')
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(z==1)
{
Union(x,y);
}
else
{
x=Find(x);
y=Find(y);
s[x].insert(y);
s[y].insert(x);
}
}
else
{
int x,y;
scanf("%d%d",&x,&y);
x=Find(x);
y=Find(y);
if(x==y) printf("1\n");
else if(s[x].count(y)==1||s[y].count(x)==1) printf("2\n");//在对应的set里面的找到了相应元素
//证明这两个元素是不同的
else printf("3\n");
}
}
}
E - 小埋的steam愿望单
Time Limit: 2000 MS Memory Limit: 64 MB
小埋有一个steam愿望单,上面记载着她想买的游戏!现在小埋有以下 $n$ 个操作:
$1\ x\ y$ 添加一个价格为 $y$ 名字为 $x$ 的游戏加入愿望单
$2\ x$ 删除名字为 $x$ 的游戏
$3\ x\ y$ 名字为 $x$ 的游戏价格调整为 $y$
$4\ x$ $x$ 为 $1$ 输出最便宜的游戏的名字(如果有多个同价格游戏输出字典序最小的),$x$ 为 $2$ 输出最贵的游戏(如果有多个同价格游戏输出字典序最大的)
不合法的情况请忽略该操作
不合法的情况请忽略该操作
不合法的情况请忽略该操作
Input
第一行一个$n(1\le n \le 1e5)$接下来 $n$ 行 每行一个 $p$ 表示操作类型 接下来根据操作类型跟一个或两个数字( $x$ 只包括大小写字母和下划线并不超过25个字符,$1\le y\le 1e9$)
Output
对于每个查询,输出一行对应游戏的名字
Sample input and output
Sample Input | Sample Output |
---|---|
8 1 slay_the_spire 60 1 dark_soul_III 118 1 The_Binding_of_Isaac 58 1 Age_of_Empires_II 88 4 1 3 dark_soul_III 57 4 1 4 2 | The_Binding_of_Isaac dark_soul_III Age_of_Empires_II |
7 4 1 1 I 100 1 II 150 1 I 200 4 1 2 III 3 IV 600 |
#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
struct node
{
int v;
string name;
friend bool operator < (node a,node b)
{
if(a.v==b.v) return a.name<b.name;
else return a.v<b.v;
}
} c[100009];
set<node>s;
map<string,int>m;
int main()
{
int n,ans=1;
scanf("%d",&n);
while(n--)
{
int flag,v;
string a;
node temp;
scanf("%d",&flag);
if(flag==1)
{
cin>>a>>v;
if(m.count(a)==0)
{
temp.name=a;
temp.v=v;
s.insert(temp);
m[a]=ans;
c[ans++]=temp;
}
}
else if(flag==2)
{
cin>>a;
if(m.count(a)==1)
{
s.erase(c[m[a]]);
m.erase(a);
}
}
else if(flag==3)
{
cin>>a>>v;
if(m.count(a)==1)
{
s.erase(c[m[a]]);
c[m[a]].v=v;
s.insert(c[m[a]]);
}
}
else
{
set<node>::iterator it;
cin>>v;
if(s.size())
{
if(v==1)
{
it=s.begin();
cout<<(*it).name<<endl;
}
else
{
it=s.end();
it--;
cout<<(*it).name<<endl;
}
}
}
}
}
三澄美琴是一个热爱学习的法医,今天晚上她准备下载很多很多法医论文资料。下面有三个操作
1 ti ai bi
i 在 ti时刻加入了编号为 ai耗时为 bi的论文进入下载队列
2 ti
在 ti时刻取消队列首位的任务(如果下载队列为空就忽略该操作)
3 ti
查询在 ti时刻队列首位的任务编号,无下载任务输出 -1
Input
第一行一个n
接下来 n行,每行 j(1≤j≤3),ti(1≤ti≤1e9,升序输出),当 j=1 时, ti 后会跟 ai(1≤ai≤n), bi(1≤bi≤1e4)
Output
对于每个3号操作输出一行,输出目前队列首位的论文编号
Sample input and output
Sample Input | Sample Output |
---|---|
6 1 1 1 5 3 2 1 3 2 3 2 4 3 5 3 6 | 1 2 -1 |
Hint
样例解释
操作一:在第1秒加入了编号为1的论文,耗时5秒,将于第6秒完成
操作二:在第1秒查询,队列首位为编号1
操作三:在第3秒加入编号为2的论文下载,耗时3秒,将于第6秒完成,队列里有两个下载任务,队列首位为编号1
操作四:在第4秒移除了编号为1的论文,目前队列里有一个下载任务,队列首位为编号2
操作五:在第5秒查询,队列首位为编号2
操作六:在第6秒查询,此时2号任务刚好完成,队列为空,输出-1
#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
struct node
{
int id,t;
};
queue<node>q;
int main()
{
int m;
scanf("%d",&m);
while(m--)
{
int op,begin,t;
node temp;
scanf("%d",&op);
if(op==1)
{
cin>>begin>>temp.id>>t;
temp.t=begin+t;
q.push(temp);
}
else if(op==2)
{
cin>>t;
while(!q.empty())
{
if(t>=q.front().t) q.pop();
else
{
q.pop();
break;
}
}
}
else
{
cin>>t;
while(!q.empty())
{
if(t>=q.front().t) q.pop();
else
{
printf("%d\n",q.front().id);
break;
}
}
if(q.empty()) printf("-1\n");
}
}
}
A.单点修改+区间求和+区间最大最小值单点修改直接把点当作区间去处理,点x为[x,x],然后修改询问最值的时候用一个全局变量去带回。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define mod 10007
#define maxn 4000009
#define L rt<<1
#define R rt<<1|1
typedef long long ll;
using namespace std;
int n;
ll sum[maxn];
ll Min[maxn];
ll Max[maxn];
ll ans1,ans2;
void pushup(int rt)
{
sum[rt]=sum[L]+sum[R];
Max[rt]=max(Max[L],Max[R]);
Min[rt]=min(Min[L],Min[R]);
}
ll query(int a,int b,int l,int r,int rt)
{
ll ans=0;
if(a<=l&&r<=b)
{
ans1=max(ans1,Max[rt]);
ans2=min(ans2,Min[rt]);
return sum[rt];
}
int mid=(l+r)>>1;
if(a<=mid) ans+=query(a,b,l,mid,L);
if(mid+1<=b) ans+=query(a,b,mid+1,r,R);
return ans;
}
void update(int a,int b,int l,int r,int rt,int c)
{
if(a<=l&&r<=b)
{
sum[rt]=c;
Max[rt]=c;
Min[rt]=c;
return ;
}
int mid=(l+r)/2;
if(a<=mid) update(a,b,l,mid,L,c);
if(mid+1<=b) update(a,b,mid+1,r,R,c);
pushup(rt);
}
int main()
{
int m;
scanf("%d%d",&n,&m);
while(m--)
{
int a,b,op;
ll c;
scanf("%d%d%d",&op,&a,&b);
if(op==0) update(a,a,1,n,1,b);
else
{
ans1=-9999999999;
ans2=INF;
printf("%lld\n",query(a,b,1,n,1)-ans1-ans2);
}
}
}
B.区间修改+区间求和线段树
这里线段树的区间修改直接写就可以了,不管加数是正数还是负数,atag[]数组标记的就就是它的加数,所以下推的时候根节点atag[rt]!=0的时候才需要下推
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define mod 10007
#define maxn 4000009
#define L rt<<1
#define R rt<<1|1
typedef long long ll;
using namespace std;
int n;
ll sum[maxn];
ll atag[maxn];
int g[maxn];
void pushup(int rt)
{
sum[rt]=sum[L]+sum[R];
}
void pushdown(int rt,int ln,int rn)
{
if(atag[rt])
{
//下推标记
sum[L]+=ln*atag[rt];
sum[R]+=rn*atag[rt];
//让左右子区间加上父节点的标记
atag[L]+=atag[rt];
atag[R]+=atag[rt];
//这里应该是累加,因为标记是累加标记
atag[rt]=0;
}
}
void update(int a,int b,int l,int r,int rt,ll c)//[a,b]代表操作区间,就是要修改的区间,l,r表示当前节点rt表示的区间
{
if(a<=l&&r<=b)
{
sum[rt]+=(r-l+1)*c;
atag[rt]+=c;
return ;
}
int mid=(l+r)>>1;
pushdown(rt,mid-l+1,r-mid);
if(a<=mid) update(a,b,l,mid,L,c);//如果左子区间和操作区间有重叠,那么就要递归左区间
if(mid+1<=b) update(a,b,mid+1,r,R,c);
pushup(rt);//左右区间更新了,同时也要更新该节点
}
ll query(int a,int b,int l,int r,int rt)
{
ll ans=0;
if(a<=l&&r<=b)
{
return sum[rt];
}
int mid=(l+r)>>1;
pushdown(rt,mid-l+1,r-mid);
if(a<=mid) ans+=query(a,b,l,mid,L);
if(mid+1<=b) ans+=query(a,b,mid+1,r,R);
return ans;
}
int main()
{
int m;
scanf("%d%d",&n,&m);
while(m--)
{
int a,b,op;
ll c;
scanf("%d%d%d%lld",&op,&a,&b,&c);
if(op==0) update(a,b,1,n,1,c);
else printf("%lld\n",query(a,b,1,n,1));
}
}
C.设 xem 表示集合中最小的未出现的正整数,如 xem{}=1,xem{1,3,4}=2
定义
bi=xem{bi−ci,bi−ci+1,...,bi−1},i=1,2,...,n
特别的,b0=1
给定 n
和 c1,c2,...,cn,请你计算出 b1,b2,...,bn.https://blog.youkuaiyun.com/weixin_39302444/article/details/80470471这题解讲的很清晰
题意要理解清楚,首先解决边界条件,到b[n]的时候最大值应该是n+1,因为0-n的最小没出现过的整数,所以最大值为n+1,那么接着我们要设last[x]=,表示x出现的最后一个位置为i,所以我们要找到最小的last[j]<i-c[i]就是j最小,那么我们怎么找到最小值呢,这题目就转换为线段树求最小值,单点更新+最小单点查找。这里强调一下,单点更新最后应该是Min[rt]=x,这里应该是rt才对,因为rt所在的节点代表了当前区间[l,l],所以这就是单点更新的边界条件,然后如何查询,那么直接用Min[L]<x来判断即可,这里是优先找左边,所以最终一定找到的是最小符合的值
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define mod 10007
#define maxn 4000009
#define L rt<<1
#define R rt<<1|1
typedef long long ll;
using namespace std;
int n,m;
int val[maxn];
int v[maxn];
int blo[maxn];
int cnt[maxn];
vector<int>ve[1009];
map<int,int>mp;
int f[1005][1005];
int ans;
int Min[maxn];
int c[maxn];
void pushup(int rt)
{
Min[rt]=min(Min[L],Min[R]);
}
void update(int l,int r,int rt,int pos,int x)//单点更新
{
if(l==r)
{
Min[rt]=x;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) update(l,mid,L,pos,x);
else update(mid+1,r,R,pos,x);
pushup(rt);
}
void query(int l,int r,int rt,int x)
{
if(l==r)
{
ans=l;
return ;
}
int mid=(l+r)>>1;
if(Min[L]<x) query(l,mid,L,x);
else query(mid+1,r,R,x);
}
int main()
{
int i;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&c[i]);
}
memset(Min,-1,sizeof(Min));
update(1,n+1,1,1,0);
for(i=1;i<=n;i++)
{
query(1,n+1,1,i-c[i]);
update(1,n+1,1,ans,i);
printf("%d ",ans);
}
}
D.一棵复杂的线段树,给n个元素a[i],对区间进行排序,有两种操作,从小到大或者从大到小,求最后第k个元素a[k].那么这道题目要去遍历一遍原来的a[i],看是否最后的结果第k个元素等于a[i],那么我们可以排序一下,然后对a[i]进行二分,对于一个a[i],用一个线段树去维护区间内大于等于a[i]的个数,也就是我们将大于等于当前二分的a[i]的原数组内所有的元素都标记为1,小于它的都标记为0,所以维护的sum[rt]就是区间内1的个数,也就是大于等于a[i]的个数,排序操作就是,如果从小到大排序,就是将小的放前面,大的放到后面,所以我们先查询[a,b]区间内有多少个1,然后对[a,b]进行排序,[a,b-num]为0,[b-num+1,b]为1这就是从小到大的排序,从大到小也是大的放前面,小的放后面就好了,这里要特判一下,如果num=0的话,那么就继续循环就好了,因为排序没有任何意义,这就是线段树更新的函数,然后最后查询,如果在第k位的值为1,那么也就是意味第k位的值应该大于等于a[i]的,所以向右查询,如果为0,那么就是第k位的值应该小于a[i]的,所以向左找,最后就可以找到答案了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define mod 10007
#define maxn 4000009
#define L rt<<1
#define R rt<<1|1
typedef long long ll;
using namespace std;
int n,m;
int v[maxn];
int atag[maxn];
int ans;
struct node
{
int op,a,b;
}c[maxn];
int p[maxn];
int sum[maxn];
void pushup(int rt)
{
sum[rt]=sum[L]+sum[R];
}
void pushdown(int rt,int ln,int rn)
{
if(atag[rt]!=-1)
{
sum[L]=ln*atag[rt];
sum[R]=rn*atag[rt];
atag[L]=atag[rt];
atag[R]=atag[rt];
atag[rt]=-1;
}
}
void build(int l,int r,int rt,int val)
{
atag[rt]=-1;
if(l==r)
{
if(v[l]>=val) sum[rt]=1;
else sum[rt]=0;
return ;
}
int mid=(l+r)/2;
build(l,mid,L,val);
build(mid+1,r,R,val);
pushup(rt);
}
void update(int a,int b,int l,int r,int rt,int val)
{
if(a<=l&&r<=b)
{
sum[rt]=val*(r-l+1);
atag[rt]=val;
return ;
}
int mid=(l+r)/2;
pushdown(rt,mid-l+1,r-mid);
if(a<=mid) update(a,b,l,mid,L,val);
if(mid+1<=b) update(a,b,mid+1,r,R,val);
pushup(rt);
}
int Q(int a,int b,int l,int r,int rt)
{
int cnt=0;
if(a<=l&&r<=b)
{
return sum[rt];
}
int mid=(l+r)>>1;
pushdown(rt,mid-l+1,r-mid);
if(a<=mid) cnt+=Q(a,b,l,mid,L);
if(mid+1<=b) cnt+=Q(a,b,mid+1,r,R);
return cnt;
}
void query(int l,int r,int rt,int pos)
{
if(l==r)
{
ans=sum[rt];
return;
}
int mid=(l+r)/2;
pushdown(rt,mid-l+1,r-mid);
if(pos<=mid) query(l,mid,L,pos);
else query(mid+1,r,R,pos);
}
int main()
{
int k,i;
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++)
{
scanf("%d",&v[i]);
p[i]=v[i];
}
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&c[i].op,&c[i].a,&c[i].b);
}
sort(p+1,p+n+1);
int l=1,r=n,mid;
while(l<=r)
{
mid=(l+r)/2;
build(1,n,1,p[mid]);
for(i=1;i<=m;i++)
{
int num=Q(c[i].a,c[i].b,1,n,1);
if(num==0) continue;
if(!c[i].op)
{
update(c[i].a,c[i].b,1,n,1,0);
update(c[i].b-num+1,c[i].b,1,n,1,1);
}
else
{
update(c[i].a,c[i].b,1,n,1,0);
update(c[i].a,c[i].a+num-1,1,n,1,1);
}
}
query(1,n,1,k);
if(ans) l=mid+1;
else r=mid-1;
}
printf("%d\n",p[l-1]);
}
H中堂系的困难任务,哈夫曼树dp递推式,看不出来...不过用优先队列来用最基本的贪心思想去码还是比较简单的
哈夫曼树#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define mod 10007
#define maxn 4000009
#define L rt<<1
#define R rt<<1|1
typedef long long ll;
using namespace std;
int n,m;
int v[maxn];
int atag[maxn];
int ans;
ll a;
priority_queue<ll,vector<ll>,greater<ll> >q;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%lld",&a);
q.push(a);
}
ll sum=0;
for(int i=1; i<=n-1; i++)
{
int temp=0;
temp+=q.top();
q.pop();
temp+=q.top();
q.pop();
q.push(temp);
sum+=temp;
}
printf("%lld\n",sum);
while(!q.empty()) q.pop();
}
}
O.帆宝RMQ 区间加法+找到x的左右端点,就和hwzer的分块入门一样,直接对块排序,然后对每个块都找一遍lower_bound(),脑残码歪了分块,疯狂wa test1,原因是lo[a]没有*m...可能multiset的复杂度太高了...TLE,有时间再去研究研究,最后以这道题为uestc数据结构专题的结尾吧,还有几题以后再补了,现在最主要是考试了....
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define mod 10007
#define maxn 100099
#define L rt<<1
#define R rt<<1|1
typedef long long ll;
using namespace std;
int n,m;
struct node
{
int id;
int x;
} v[maxn];
int blo[maxn];
ll atag[maxn];
vector<node>ve[maxn];
bool cmp(const node &a,const node &b)
{
if(a.x==b.x) return a.id<b.id;
else return a.x<b.x;
}
void reset(int x)
{
ve[x].clear();
for(int i=(x-1)*m+1; i<=min(n,x*m); i++)
{
ve[x].push_back(v[i]);
}
sort(ve[x].begin(),ve[x].end(),cmp);
}
int query(int c)
{
int Min=INF;
int Max=-1;
for(int i=1; i<=blo[n]; i++)
{
node temp;
temp.id=0;
temp.x=c-atag[i];
vector<node>::iterator it;
it=lower_bound(ve[i].begin(),ve[i].end(),temp,cmp);
if(it==ve[i].end()) continue;
else
{
while(it!=ve[i].end())
{
if((*it).x==temp.x)
{
Min=min(Min,(*it).id);
Max=max(Max,(*it).id);
}
else break;
it++;
}
}
}
if(Min==INF||Max==-1) return -1;
else
{
return Max-Min;
}
}
void add(int a,int b,int c)
{
int i;
for(i=a; i<=min(b,blo[a]*m); i++)
{
v[i].x+=c;
}
reset(blo[a]);
if(blo[a]!=blo[b])
{
for(i=(blo[b]-1)*m+1; i<=b; i++)
{
v[i].x+=c;
}
reset(blo[b]);
}
for(i=blo[a]+1; i<=blo[b]-1; i++)
{
atag[i]+=c;
}
}
int main()
{
int i,k;
scanf("%d%d",&n,&k);
m=sqrt(n);
for(i=1; i<=n; i++)
{
scanf("%lld",&v[i].x);
v[i].id=i;
blo[i]=(i-1)/m+1;
ve[blo[i]].push_back(v[i]);
}
for(i=1; i<=blo[n]; i++) reset(i);
for(i=1; i<=k; i++)
{
int op,a,b;
ll c;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
else
{
scanf("%d",&c);
printf("%d\n",query(c));
}
}
}