Simone and graph coloring
题意:给定一个排列,要求给逆序对连边,形成一张图,图的相邻顶点之间颜色不能相同,问最少的颜色种类以及每个点的颜色。
做法:对于每一个数,他要连接上在他前面出现过的比他大的数。如果他前面出现过1 2 3 4 这种颜色,那他的颜色必定是5。可以递归的想,他前面出现过的最大颜色是4,那么对于涂了颜色4的,肯定是出现过1 2 3 的颜色的…,所以线段树查询区间最大值,从而确定当前的颜色。
因为多测数据有1e6组,用endl会狂TLE,所以以后再也不要用endl了。
#include<bits/stdc++.h>
using namespace std;
// #define int long long
const int N = 1e6 + 5, mod = 998244353;
stack<int> q;
int a[N];
bool f[N];
struct node{
int l, r;
int ma;
#define l(x) tree[x].l
#define r(x) tree[x].r
} tree[N * 4];
void build(int p, int l, int r)
{
tree[p].l = l, tree[p].r = r;
if (l >= r) {
tree[p].ma = 0;
return;
}
int mid = (l + r) >> 1;
int chl = p * 2, chr = p * 2 + 1;
build(chl, l, mid);
build(chr, mid + 1, r);
tree[p].ma = max(tree[chl].ma, tree[chr].ma);
}
void up(int p, int l, int r, int x)
{
if (l <= l(p) && r >= r(p)) {
tree[p].ma = x;
return;
}
int mid = (l(p) + r(p)) >> 1;
int chl = p * 2, chr = p * 2 + 1;
if (l <= mid) up(chl, l, r, x);
if (mid < r) up(chr, l, r, x);
tree[p].ma = max(tree[chl].ma, tree[chr].ma);
}
int que(int p, int l, int r)
{
if (l <= l(p) && r >= r(p)) {
return tree[p].ma;
}
int mid = (l(p) + r(p)) >> 1;
int chl = p * 2, chr = p * 2 + 1;
int ret = 0;
if (l <= mid) ret = max(ret,que(chl, l, r));
if (mid < r) ret = max(ret,que(chr, l, r));
return ret;
}
signed main ()
{
int tt;
cin >> tt;
while(tt--){
int n;
scanf("%d", &n);
build(1, 1, n);
for (int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
int k = que(1, x, n);
a[i] = k + 1;
up(1, x, x, k + 1);
f[k + 1] = 1;
}
int cnt = 0;
for (int i = 1; i <= n + 1; i++) {
if (!f[i]) {
cnt = i - 1;
break;
}
}
printf("%d\n", max(1, cnt));
for (int i = 1; i <= n; i++) printf("%d ", a[i]);
puts("");
for (int i = 1; i <= n; i++) {
a[i] = 0;
f[i] = 0;
}
}
return 0;
}
Mr. Main and Windmills
题意:给定起始一条线段与n个点,m次询问,每次回答第h个点与其他n-1个点两两相连,得到与线段的一些交点,输出第k个交点。
做法:直接套用几何板子即可。
坑点:这条线段是矢量,有方向!!所以第k个交点要分清楚是左到右还是右到左。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, mod = 998244353;
#define eps 1e-8
#define pi 3.141592653589793
using namespace std;
//二维点类
struct Point
{
double x,y;
bool operator < (const Point &k) const {
return x < k.x;
}
bool operator <= (const Point &k) const {
return (x < k.x) || (fabs(x - k.x) <= 1e-5);
}
Point(double a=0,double b=0){x=a;y=b;}
};
typedef Point Vector;
//二维直线类,一般方程ax+by+c=0
struct Line
{
double a,b,c,angle;
Point p1,p2;
Line(Point s,Point e)
{
a=s.y-e.y;
b=e.x-s.x;
c=s.x*e.y-e.x*s.y;
angle=atan2(e.y-s.y,e.x-s.x);
p1=s;p2=e;
}
Line(){}
};
//二维线段类
struct Segment
{
Point s,e;
Segment(Point a,Point b){s=a;e=b;}
Segment(double x1,double y1,double x2,double y2)
{
s=Point(x1,y1);
e=Point(x2,y2);
}
Segment(){}
};
//向量的加减及数乘
Vector operator + (Point a,Point b)
{
return Vector(a.x+b.x,a.y+b.y);
}
Vector operator - (Point a,Point b)
{
return Vector(a.x-b.x,a.y-b.y);
}
Vector operator * (Point a,double k)
{
return Vector(a.x*k,a.y*k);
}
Vector operator / (Point a,double k)
{
return Vector(a.x/k,a.y/k);
}
//求向量的模(长度)
double len(Vector a)
{
return sqrt(a.x*a.x+a.y*a.y);
}
//得到sp-op和ep-op的叉积
//>0时ep在opsp的逆时针方向,<0时顺时针,=0时共线
double Cross(Point &sp, Point &ep, Point &op)
{
return (sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y);
}
//两向量求叉积,求三角形面积需要除以2
double Cross(Vector a,Vector b)
{
return a.x*b.y-b.x*a.y;
}
//两向量求点积
double Dot(Vector a,Vector b)
{
return a.x*b.x+a.y*b.y;
}
//求最大最小值
double max(double a,double b)
{
if (a<b) return b; else return a;
}
double min(double a,double b)
{
if (a>b) return b; else return a;
}
//采用eps的精度判断大/小于零
int epssgn(double x)
{
if (fabs(x)<eps) return 0;
else return x<0?-1:1;
}
//求两点之间的直线距离
double dis(Point a,Point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
//判断两直线是否平行
int Parallel(Line l1,Line l2)
{
return (fabs(l1.a*l2.b-l2.a*l1.b)<eps);
}
//判断两直线是否相等
int LineEqual(Line l1,Line l2)
{
if (!Parallel(l1,l2)) return 0;
else return (fabs(l1.a*l2.c-l2.a*l1.c)<eps && fabs(l1.b*l2.c-l2.b*l1.c)<eps);
}
//求点C到线段AB的距离
double PointToSegDist(Point A,Point B,Point C)
{
if (dis(A,B)<eps) return dis(B,C);
if (epssgn(Dot(B-A,C-A))<0) return dis(A,C);
if (epssgn(Dot(A-B,C-B))<0) return dis(B,C);
return fabs(Cross(B-A,C-A))/dis(B,A);
}
//求线段两端AB到另一线段CD的距离
double TwoSegMinDist(Point A,Point B,Point C,Point D)
{
return min(min(PointToSegDist(A,B,C),PointToSegDist(A,B,D)),
min(PointToSegDist(C,D,A),PointToSegDist(C,D,B)));
}
Point SymPoint(Point p,Line l) //求二维平面上点p关于直线p1p2的对称点
{
Point result;
double a=l.p2.x-l.p1.x;
double b=l.p2.y-l.p1.y;
double t=((p.x-l.p1.x)*a+(p.y-l.p1.y)*b)/(a*a+b*b);
result.x=2*l.p1.x+2*a*t-p.x;
result.y=2*l.p1.y+2*b*t-p.y;
return result;
}
//判断线段s1e1与s2e2是否相交(含端点)
//不含端点的话将下面的<=改成<
int IsSegmentIntersect(Point s1, Point e1, Point s2, Point e2)
{
if( min(s1.x,e1.x)<= max(s2.x,e2.x) &&
min(s1.y,e1.y)<= max(s2.y,e2.y) &&
min(s2.x,e2.x)<= max(s1.x,e1.x) &&
min(s2.y,e2.y)<= max(s1.y,e1.y) &&
Cross(s2,e2,s1)*Cross(s2,e2,e1)<=0 &&
Cross(s1,e1,s2)*Cross(s1,e1,e2)<=0)
return 1;
return 0;
}
//知道直线上两点p1p2,判断直线与线段se是否相交,含顶点
int IsLineIntersectSegment(Point p1,Point p2,Point s,Point e)
{
if (Cross(p1,p2,s)*Cross(p1,p2,e)>eps) return 0;
else return 1;
}
int IsLineIntersectSegment(Line l1,Point s,Point e)
{
if (Cross(l1.p1,l1.p2,s)*Cross(l1.p1,l1.p2,e)>eps) return 0;
else return 1;
}
//求两条直线l1和l2的交点
Point GetIntersect(Line l1, Line l2)
{
Point res;
res.x=(l1.b*l2.c-l2.b*l1.c)/(l1.a*l2.b-l2.a*l1.b);
res.y=(l1.c*l2.a-l2.c*l1.a)/(l1.a*l2.b-l2.a*l1.b);
return res;
}
Point arr[N], ans[N];
Point ss, tt;
bool cmp(Point &x, Point &y)
{
return dis(x, ss) < dis(y, ss);
}
signed main ()
{
int n, m;
scanf("%lld%lld", &n, &m);
cin >> ss.x >> ss.y >> tt.x >> tt.y;
Line L = Line(ss, tt);
for (int i = 1; i <= n; i++)
{
scanf("%lf%lf", &arr[i].x, &arr[i].y);
}
while(m--)
{
int h, k;
scanf("%lld%lld", &h, &k);
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (i == h) continue;
Line t = Line(arr[i], arr[h]);
if (!IsLineIntersectSegment(arr[i], arr[h], ss, tt)) continue;
ans[++cnt] = GetIntersect(L, t);
}
sort(ans + 1, ans + 1 + cnt, cmp);
if (k > cnt) puts("-1");
else {
printf("%.10f %.10f\n", ans[k].x, ans[k].y);
}
}
return 0;
}
Parallel Sort
题意:给定一个排列,在一次操作中,可以选出k对下标,这些下标两两不同,对于每一对,交换他们指向的数。要求操作次数最少。
做法:以前做过类似的交换排列的题,所以想到 i 与 pi连边来做。但是这题烦就烦在要求次数最少。对于一个长度为n的环,可以进行n-1断边,也就是交换。那么一个环怎么操作次数最少呢?就是说有很多边可以同一次操作的时候断开。
草稿纸画了好久。。终于找到一个方法,两次操作就能达到目的。。
具体方法是,p1p2…pk-1pk,交换p1与pk-1,那么pk-1与pk形成环,与前面的断开,这时候前面的就可以p2交换pk-2,得到p1与pk-2形成的环…
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 998244353;
int a[N], b[N], pos[N];
vector<int> v[N], ans[N], v2[N];
bool vis[N];
int cnt;
void dfs1(int k)
{
vis[k] = 1;
b[++cnt] = k;
for (auto i : v[k]) {
if (vis[i]) continue;
dfs1(i);
}
}
void dfs2(int k)
{
vis[k] = 1;
b[++cnt] = k;
for (auto i : v2[k]) {
if (vis[i]) continue;
dfs2(i);
}
}
signed main ()
{
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
pos[a[i]] = i;
}
for (int i = 1; i <= n; i++) {
int x = i, y = a[i];
if (x == y) continue;
x = pos[x], y = pos[y];
v[x].push_back(y);
v[y].push_back(x);
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
cnt = 0;
dfs1(i);
if (cnt == 2) {
ans[1].push_back(b[1]);
ans[1].push_back(b[2]);
// cout << "?";
continue;
}
if (cnt <= 1) continue;
int l = 1, r = cnt;
ans[1].push_back(b[1]);
ans[1].push_back(b[cnt - 1]);
v2[b[cnt - 1]].push_back(b[cnt]);
v2[b[cnt]].push_back(b[cnt - 1]);
r -= 2;
while(l < r)
{
if (l + 1 < r){
ans[1].push_back(b[l + 1]);
ans[1].push_back(b[r]);
}
if (l != r){
v2[b[l]].push_back(b[r]);
v2[b[r]].push_back(b[l]);
}
l++;
r--;
}
}
}
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
cnt = 0;
dfs2(i);
if (cnt == 2) {
ans[2].push_back(b[1]);
ans[2].push_back(b[2]);
continue;
}
}
}
int num = 0;
if (ans[1].size()) num = 1;
if (ans[2].size()) num = 2;
cout << num << "\n";
for (int i = 1; i <= num; i++) {
cout << ans[i].size() / 2 << " ";
for (auto j : ans[i]) cout << j << " ";
puts("");
}
return 0;
}