做了一些莲辉挂的CF题。。选一些来讲讲。
D. Guess Your Way Out! II
Amr bought a new video game "Guess Your Way Out! II". The goal of the game is to find an exit from the maze that looks like a perfect binary tree of heighth. The player is initially standing at the root of the tree and the exit from the tree is located at some leaf node.
Let's index all the nodes of the tree such that
- The root is number 1
- Each internal node i (i ≤ 2h - 1 - 1) will have a left child with index =2i and a right child with index = 2i + 1
The level of a node is defined as 1 for a root, or1 + level of parent of the node otherwise. The vertices of the levelh are called leaves. The exit to the maze is located at some leaf noden, the player doesn't know where the exit is so he has to guess his way out!
In the new version of the game the player is allowed to ask questions on the format "Does theancestor(exit, i) node number belong to the range[L, R]?". Here ancestor(v, i) is the ancestor of a node v that located in the level i. The game will answer with "Yes" or "No" only. The game is designed such that it doesn't always answer correctly, and sometimes it cheats to confuse the player!.
Amr asked a lot of questions and got confused by all these answers, so he asked you to help him. Given the questions and its answers, can you identify whether the game is telling contradictory information or not? If the information is not contradictory and the exit node can be determined uniquely, output its number. If the information is not contradictory, but the exit node isn't defined uniquely, output that the number of questions is not sufficient. Otherwise output that the information is contradictory.
The first line contains two integers h, q (1 ≤ h ≤ 50,0 ≤ q ≤ 105), the height of the tree and the number of questions respectively.
The next q lines will contain four integers eachi, L, R, ans (1 ≤ i ≤ h,2i - 1 ≤ L ≤ R ≤ 2i - 1,), representing a question as described in the statement with its answer (ans = 1 if the answer is "Yes" and ans = 0 if the answer is "No").
If the information provided by the game is contradictory output "Game cheated!" without the quotes.
Else if you can uniquely identify the exit to the maze output its index.
Otherwise output "Data not sufficient!" without the quotes.
3 1 3 4 6 0
7
4 3 4 10 14 1 3 6 6 0 2 3 3 1
14
4 2 3 4 6 1 4 12 15 1
Data not sufficient!
4 2 3 4 5 1 2 3 3 1
Game cheated!
Node u is an ancestor of node v if and only if
- u is the same node as v,
- u is the parent of node v,
- or u is an ancestor of the parent of nodev.
In the first sample test there are 4 leaf nodes 4, 5, 6, 7. The first question says that the node isn't in the range [4, 6] so the exit is node number 7.
In the second sample test there are 8 leaf nodes. After the first question the exit is in the range[10, 14]. After the second and the third questions only node number14 is correct. Check the picture below to fully understand.
题意大概就是给你一颗满二叉树,告诉你在第h层有一个出口。接下来给你q个信息。
每个信息包含真实出口所在的祖先层区间(或者自己所在层的区间),并告诉你这个消息要么是假的,否则就是真的。
然后全部信息给你后,让你判断出口是不是唯一。
思路:这道题不就是集合的题目么= =。
首先:我们把祖先区间映射到相应的层区间--->可以预处理出来。然后对于Q个消息中,若干个为真的消息,我们维护一下求这些区间里的交集--->显然有且只有一个集合的情况下才可能有解,否则比如告诉你真的消息映射到相应层区间后有两个,比如图中的8-10 11-14均为真,那么就输出电脑作弊咯。如果维护的区间只有一个,那么我们进行下一步的操作。对于剩余的Q个消息里的我们都知道是假消息,所以对这些消息,我们可以求这些给定区间的补集,然后对这些补集求一个并集,那么说明答案会在这个并集的区间里。那么经过这两个操作后得到的集合,就是出口所在的集合,然后再在这两个集合中求一个交集,判断元素是不是只有一个,如果只有一个,那么维护的最终结果就是出口。如果大于一个结果,那么就是说明题目给的数据矛盾了。
用数学语言描述就是,对于每一个真的消息,求出这些个真的消息的交集A,对于每一个假的消息,求出每一个消息的并集C,然后对每个补集求一个并集得到B,最后A和B求一个交集,判断最后集合中最后的元素是否唯一就好了。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
struct node{
long long l;
long long r;
};
bool cmp(node a,node b)
{
return a.l<b.l;
}
long long L,R;
vector<node> a,b,c;
long long h,q;
long long deep,ll,rr;
bool ok;
void solve()
{
sort(a.begin(),a.end(),cmp);
long long maxL=L,minR=R;
for(long long i=0;i<a.size();i++)
{
maxL=max(maxL,a[i].l);
minR=min(minR,a[i].r);
}//A集合求交
if(maxL>minR)
{
puts("Game cheated!");
return ;
}//这个的意思等价于出现了不相交的集合,那么就出现了BUG。。
sort(b.begin(),b.end(),cmp);
long long tmpl=0;
long long tmpr=0;
c.push_back((node){L-1,L-1});//假设并集的左区间是它。
for(long long i=0;i<b.size();i++)
{
if(i==0)
{
tmpl=b[i].l;
tmpr=b[i].r;
}
else
{
if(b[i].l>tmpr)
{
c.push_back((node){tmpl,tmpr});
tmpl=b[i].l;
tmpr=b[i].r;
}
else
{
tmpr=max(tmpr,b[i].r);
}
}
if(i==b.size()-1)
{
c.push_back((node){tmpl,tmpr});
}//每次更新并集的最左和最右区间
}
c.push_back((node){R+1,R+1});//并集的最右区间为这个。
b.clear();
for(long long i=0;i+1<c.size();i++)
{
if(c[i+1].l-c[i].r>1)
{
b.push_back((node){c[i].r+1,c[i+1].l-1});
}
}//求并集的补集,重新放入B集合,现在B中的集合区间也都是真的了。
long long cnt=0;
long long res;
for(long long i=0;i<b.size();i++)
{
long long tl=max(maxL,b[i].l);
long long tr=min(minR,b[i].r);//重新更新所有真区间的交集的左右区间。
if(tl<=tr)
{
res=tl;
cnt+=tr-tl+1;//记录下来可能的元素个数
}
}
if(cnt==0)
{
puts("Game cheated!");
}
else if(cnt>1)
{
puts("Data not sufficient!");
}
else
{
cout<<res<<endl;
}
return ;
}
int main()
{
cin>>h>>q;
long long dep=(long long )1;
L=1;
R=1;
while(dep<h)
{
L=L*2;
R=R*2+1;
dep++;
}//处理出h层的最左的端点和最右的端点。
while(q--)
{
cin>>deep>>ll>>rr>>ok;
while(deep<h)
{
ll=ll*2;
rr=rr*2+1;
deep++;
}//映射到h层的子区间。
if(ok)
{
// cout<<ll<<"~~"<<rr<<endl;
a.push_back((node){ll,rr});//是真的就放到集合A里去。
}
else
{
b.push_back((node){ll,rr});//否则先放入集合B
}
}
solve();
return 0;
}
Leonid wants to become a glass carver (the person who creates beautiful artworks by cutting the glass). He already has a rectangularw mm × h mm sheet of glass, a diamond glass cutter and lots of enthusiasm. What he lacks is understanding of what to carve and how.
In order not to waste time, he decided to practice the technique of carving. To do this, he makes vertical and horizontal cuts through the entire sheet. This process results in making smaller rectangular fragments of glass. Leonid does not move the newly made glass fragments. In particular, a cut divides each fragment of glass that it goes through into smaller fragments.
After each cut Leonid tries to determine what area the largest of the currently available glass fragments has. Since there appear more and more fragments, this question takes him more and more time and distracts him from the fascinating process.
Leonid offers to divide the labor — he will cut glass, and you will calculate the area of the maximum fragment after each cut. Do you agree?
The first line contains three integers w, h, n (2 ≤ w, h ≤ 200 000,1 ≤ n ≤ 200 000).
Next n lines contain the descriptions of the cuts. Each description has the formH y or V x. In the first case Leonid makes the horizontal cut at the distancey millimeters (1 ≤ y ≤ h - 1) from the lower edge of the original sheet of glass. In the second case Leonid makes a vertical cut at distancex (1 ≤ x ≤ w - 1) millimeters from the left edge of the original sheet of glass. It is guaranteed that Leonid won't make two identical cuts.
After each cut print on a single line the area of the maximum available glass fragment in mm2.
4 3 4 H 2 V 2 V 3 V 1
8 4 4 2
7 6 5 H 4 V 3 V 5 H 2 V 1
28 16 12 6 4
Picture for the first sample test:
![](http://espresso.codeforces.com/76f76c4b91db5d63d733cd37c91bce09001259b7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6a291601dba5358407f027a28858239e.png)
思路:维护4个东西,每刀切完后现有玻璃的各种长,各种宽,切得横坐标位置,切得纵坐标位置。然后维护每一刀切下去后,消失的长度和宽度(就比如上面的第二个图,竖着切了那一刀,长度为4的就不可能再有了,所以就得删了,而长度为2的是新生成的咱们就得扔进去),以及新生成的长度和宽度。每刀之后直接算出该刀下去后可能得到的最大长度和最大宽度,乘起来就是这刀下去的答案了。
具体实现:我一开始本来是想直接用set搞的,但是发现有时候要insert一些重复的元素,因为他们是切完后继续保留在那里的,所以得加入进去,而删除元素也不能一锅端,比如第三刀下去,长度为2的被切成了两个1,但是还有个长度为2的是保留了的。。所以set的insert和erase搞不了,后来知道了还有个multiset可以插入重复的,删除也不会一锅端。那么开4个multiset暴力搞过去就好了- -。学习了一个新的黑科技啊,然后卡着时限过了2333.
不过听说这题的正常解法是并查集。。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<set>
using namespace std;
multiset<long long> wpos,hpos,wlen,hlen;
multiset<long long >::iterator it,it1,it2;
char s[10];
int w,h,n,pos;
int main()
{
cin>>w>>h>>n;
wpos.insert(0);//初始化横坐标左端0
wpos.insert(w);//初始化横坐标右端为w
hpos.insert(0);//同理。。
hpos.insert(h);//同理。。
wlen.insert(w);//初始化长度就一个
hlen.insert(h);//初始化高度也就这一个
while(n--)
{
cin>>s>>pos;
if(s[0]=='H')
{
hpos.insert(pos);//如果是H,插入这个POS,比如是2.
it=hpos.find(pos);//找到这个POS所在的位置
//cout<<*it<<endl;
it1=it2=it;
it1--;//它左边那个
it2++;//它右边那个
hlen.erase(hlen.find(*it2 - *it1));//它右边那个坐标-左边那个坐标的长度肯定是没有了。。2右边那个是3,左边那个是0,显然这刀下去3这个长度没了
hlen.insert(*it2-*it);//进而生成了这个长度。。。1
hlen.insert(*it-*it1);//和这个长度。。2
//cout<<*it2-*it1<<endl;
}
else//这个同上。
{
wpos.insert(pos);
it=wpos.find(pos);
//cout<<*it<<endl;
it1=it2=it;
it1--;
it2++;
wlen.erase(wlen.find(*it2 - *it1));
wlen.insert(*it2-*it);
wlen.insert(*it-*it1);
}
it1=wlen.end();
it2=hlen.end();
// cout<<*(--it1)<<"~~"<<*(--it2)<<endl;
long long a=*(--it1);
long long b=*(--it2);
long long ans=a*b;
cout<<ans<<endl;//找完后指教找到最后一个位置对应的值相乘就好了。
}
return 0;
}
The clique problem is one of the most well-known NP-complete problems. Under some simplification it can be formulated as follows. Consider an undirected graphG. It is required to find a subset of verticesC of the maximum size such that any two of them are connected by an edge in graphG. Sounds simple, doesn't it? Nobody yet knows an algorithm that finds a solution to this problem in polynomial time of the size of the graph. However, as with many other NP-complete problems, the clique problem is easier if you consider a specific type of a graph.
Consider n distinct points on a line. Let thei-th point have the coordinate xi and weight wi. Let's form graph G, whose vertices are these points and edges connect exactly the pairs of points(i, j), such that the distance between them is not less than the sum of their weights, or more formally:|xi - xj| ≥ wi + wj.
Find the size of the maximum clique in such graph.
The first line contains the integer n (1 ≤ n ≤ 200 000) — the number of points.
Each of the next n lines contains two numbersxi,wi (0 ≤ xi ≤ 109, 1 ≤ wi ≤ 109) — the coordinate and the weight of a point. All xi are different.
Print a single number — the number of vertexes in the maximum clique of the given graph.
4 2 3 3 1 6 1 0 2
3
If you happen to know how to solve this problem without using the specific properties of the graph formulated in the problem statement, then you are able to get a prize of one million dollars!
The picture for the sample test.
![](https://i-blog.csdnimg.cn/blog_migrate/727886f4742159a8f56d35d80eafa799.png)
题意:给你N个二元组,每个里面包含X和W,然后选出尽可能多的二元组构成一个集合,使得集合中的任意两个二元组被调出来后满足|Xi-Xj|>=Wi+Wj
求满足条件的集合的最大值。
直接来看这个不等式- -。。拆掉绝对值就变成了:
Xi-Xj>=Wi+Wj ||Xj-Xi>=Wi+Wj
移项有:
Xi-Wi>=Xj+Wj
Xj-Wj>=Xi+Wi
很幸福的发现了- -只要把这个二元组生成一个结构体,Xi-Wi为左区间,Xi+Wi为右区间。按左端点排序,直接贪心选择不相交的区间不就好了- -。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
struct node{
long long l;
long long r;
};
bool cmp(node a,node b)
{
return a.l<b.l;
}
vector<node> v;
int main()
{
long long n;
cin>>n;
for(long long i=0;i<n;i++)
{
long long x,w;
cin>>x>>w;
v.push_back((node){x-w,x+w});
}
sort(v.begin(),v.end(),cmp);
long long ans=0;
long long mina=-9999999999;
for(long long i=0;i<n;i++)
{
// node tmp=v[i];
if(mina<=v[i].l)
{
ans++;
mina=v[i].r;
}
else if(mina>v[i].r)
{
mina=v[i].r;
}
}
cout<<ans<<endl;
return 0;
}
Karafs is some kind of vegetable in shape of an1 × h rectangle. Tavaspolis people love Karafs and they use Karafs in almost any kind of food. Tavas, himself, is crazy about Karafs.
Each Karafs has a positive integer height. Tavas has an infinite 1-based sequence of Karafses. The height of the i-th Karafs issi = A + (i - 1) × B.
For a given m, let's define an m-bite operation as decreasing the height of at most m distinct not eaten Karafses by 1. Karafs is considered as eaten when its height becomes zero.
Now SaDDas asks you n queries. In each query he gives you numbersl, t andm and you should find the largest number r such that l ≤ r and sequencesl, sl + 1, ..., sr can be eatenby performing m-bite no more thant times or print -1 if there is no such numberr.
Input
The first line of input contains three integers A,B and n (1 ≤ A, B ≤ 106,1 ≤ n ≤ 105).
Next n lines contain information about queries.i-th line contains integers l, t, m (1 ≤ l, t, m ≤ 106) fori-th query.
Output
For each query, print its answer in a single line.
Sample Input
2 1 4 1 5 3 3 3 10 7 10 2 6 4 8
4 -1 8 -1
1 5 2 1 5 10 2 7 4
1 2题意:给你一个首项为A,公差为B的无穷等差数列,给你N个询问,每个询问包含这个数列的左端点l,可以改变的次数t,和最多可以从l改变的个数m,改变是指每次从l开始最多可以选取连续的m个数将其-1,问t次后,最长0序列的最右边的那个数是多少。如果T次没能把一个数变成0,就输出-1吧。。。
思路:求出左端点,二分右端点。
为什么:显然要t次内要有0出现,而数列又是递增的。。显然要看输入的左端点对应数列中的数跟比的大小,如果比t大肯定是-1。。否则就把这个值定下来不动了-->显然它最容易到0嘛。那么右端点怎么确定呢---->我们最好要让区间里所有的元素均小于等于t,这样t次变成0的个数才能尽可能的多。然后假设右端点是r,就有a1+(r-1)*b=t,求一下r就出来了。然后对右端点进行二分。怎么二分呢- -。。
显然枚举一下左端点到右端点的和是多少了就好了嘛,最大的数都小于t,那么能够选取的数里面相加肯定小于等于mt,才能让选取的这些数变成0嘛,所以找都能变成0的最后一个数的位置就好了,所以二分一下就出答案了。。。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
long long a,b,n;
long long l,t,m;
long long an(long long x)
{
return a+(x-1)*b;
}
long long sum(long long r)
{
// cout<<l<<"~~"<<r<<endl;
return (an(l)+an(r))*(r-l+1)/2;
}
int main()
{
cin>>a>>b>>n;
while(n--)
{
cin>>l>>t>>m;
if(an(l)>t)
{
cout<<-1<<endl;
continue;
}
long long left=l;
long long right=(t-a)/b+1;
long long mid;
while(left<=right)
{
mid=(left+right)/2;
if(sum(mid)<=m*t)
{
left=mid+1;
}
else right=mid-1;
}
cout<<left-1<<endl;
}
return 0;
}
还有一题单独写了博客,还有两道字符串都不会- -。其余几道YY一下很快都能暴力搞出来。。。