2021昆明

H Hard Calculation

签到

I Mr. Main and Windmills

题意:

给出n个点和一条直线,点两两之间形成一条直线,询问m次,每次询问点之间直线与原来直线的第k个交点。

做法:

求直线与线段交点。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define ff double
using namespace std;

const int N = 2e5 + 7;
const ff eps = 1e-9;
ff x[N], y[N];
struct point {
ff x,y;
};
bool pointInBox(point a,point b,point p) {
ff minx = min(a.x,b.x), maxx = max(a.x,b.x);
ff miny = min(a.y,b.y), maxy = max(a.y,b.y);
return p.x >= minx && p.x <= maxx && p.y >= miny && p.y <= maxy;
}
struct sgt{
point p1,p2;
bool contains(point &p) {
return pointInBox(p1,p2,p);
}
};
struct line {
double a,b,c;
line(ff a = 0,ff b = 0, ff c = 0) : a(a), b(b), c(c) {}
};
line getLine(ff x1, ff y1, ff x2, ff y2) {
return line(y2 - y1, x1 - x2, y1 *(x2 - x1) - x1 * (y2 - y1));
}
line getLine(point p,point q) {
return getLine(p.x,p.y,q.x,q.y);
}
point getinter(line p,line q) {
point pi;
pi.x = (q.c * p.b - p.c * q.b) / ( p.a * q.b - q.a * p.b);
pi.y = (q.c * p.a - p.c * q.a) / (p.b * q.a - q.b * p.a);
return pi;
}
ff cp(point a,point b,point c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
point pi;
bool ininter(sgt s1, sgt s2) {
line p = getLine(s1.p1,s1.p2), q = getLine(s2.p1,s2.p2);
if (fabs(p.a * q.b - q.a * p.b) < eps) {
if (fabs(cp(s1.p1,s1.p2,s2.p1)) < eps) {
return s1.contains(s2.p1) || s1.contains(s2.p2);
return false;
}
}
pi = getinter(p,q);
// return true;
return s1.contains(pi);
}
vector<point>num[N];
point st;
ff dist(point a,point b) {
ff dx = a.x - b.x, dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
bool cmp(point a, point b)
{
return dist(a,st) < dist(b,st);
}

signed main() {
int n, m;
cin >> n >> m;
ff sx, sy, tx, ty;
cin >> sx >> sy >> tx >> ty;
line star = getLine({sx,sy},{tx,ty});
sgt start = {{sx,sy},{tx,ty}};
st = {sx,sy};
for(int i = 1; i <= n; i++)
{
cin >> x[i] >> y[i];
}

for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(i == j) continue;
sgt now = {{x[i],y[i]},{x[j],y[j]}};
if (ininter(start,now)) {
num[i].push_back({pi});
}
}
}
for (int i = 1; i <= n; ++ i ) {
sort(num[i].begin(), num[i].end(), cmp);
}
while(m--)
{
int x, y;
cin >> x >> y;
if(num[x].size() < y) cout << -1 << endl;
else printf("%.7lf %.7lf\n", num[x][y - 1].x, num[x][y - 1].y);
}
return 0;
}

J Parallel Sort

题意:

给出一个n个数的排列,要求通过某种操作把它变成1~n的排列。每轮操作可以选择两个下标,交换这两个数,每轮可以选择多个下标,但下标不能重复选,问最小几轮可以达到目标状态,输出方案。

做法:

队友找规律发现最多两轮即可完成交换。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

#define int long long
#define endl '\n'
#define inc(i,a,b) for (int i = a; i <= b; ++ i )
#define dec(i,a,b) for (int i = a; i >= b; -- i)
#define eb(x,y) emplace_back(x,y)
#define m0(a) memset(a,0,sizeof a)
#define minf(a) memset(a,0x3f,sizeof a)
#define mcf(a) memset(a,0xcf,sizeof a)
using namespace std;

typedef long long ll;
typedef double ff;
typedef pair<int,int> PII;
typedef pair<double,double> PDD;
const int N = 5e5 + 7;
int a[N],b[N];
bool v[N];
int n,m;
vector<pair<int,int>> ed,st;
vector<int> e[N];
vector<int> mid;
void dfs(int u,int c) {
v[u] = 1; mid.emplace_back(u);
for (auto i : e[u]) {
if (v[i]) {
return;
}
dfs(i ,c + 1);
}
}
void solve () {
bool f = 0;
cin >> n; inc(i,1,n) cin >> a[i];
inc(i,1,n) {
if (i != a[i])
e[i].emplace_back(a[i]),f = 1;
}
if (!f) {
puts("0"); return;
}
f = 0;
inc(i,1,n) {
if (!v[i]) {
mid.clear();
dfs(i,1);
if (mid.size() == 2) {
ed.emplace_back(mid[0],mid[1]);
} else {
f = 1;
if (mid.size() & 1) {
for (int i = 1, j = mid.size() - 1; i < j; ++ i, -- j) {
st.emplace_back(mid[i], mid[j]);
swap(mid[i],mid[j]);
}
for (int i = 0, j = mid.size() - 1; i < j;++ i, -- j ) {
ed.emplace_back(mid[i],mid[j]);
}
} else {
for (int i = 1, j = mid.size() - 1; i < j ; ++ i, -- j) {
st.emplace_back(mid[i],mid[j]);
swap(mid[i],mid[j]);
}
// for (int i = 0; i + 1 < mid.size(); ++ i) ed.emplace_back(mid[i],mid[i+1]);
for (int i = 0, j = mid.size() - 1; i < j; ++ i, -- j ) {
ed.emplace_back(mid[i],mid[j]);
}
}
}
}
}
if (!f) {
cout << 1 << endl;
cout << ed.size() << " ";
for (auto i : ed) cout << i.first << " " << i.second << " ";
} else {
cout << 2 << endl;
cout << st.size() << " ";
for (auto i: st) cout << i.first << " " << i.second << " "; cout << endl;
cout << ed.size() << " ";
for (auto i : ed) cout << i.first << " " << i.second << " ";
}
}

signed main() {
int t;
// cin >> t;
// while(t--)
solve();
return 0;
}

K Riichi!!

题意:

模拟打麻将。给出当前所有的14张牌,可以选择打出一张牌,从牌堆里拿任意一张牌,输出通过这种操作可以胜利的方案,如果不需要操作就能胜利,输出"Tsumo!"。

思路:

要想达到目标状态,需要一个"quetou"和四个“shunzi” 或者"kezi"(“kezi” 的数量和"shunzi"的数量和为4)。

枚举打出哪张牌,拿哪张牌;然后枚举"quetou",check一下剩下的牌是否合法。

在check时,对于"zi",不能组成顺子,如果存在某一个"zi"的数量大于0并且不为3,直接返回false(剪枝)。

对于其他的三种牌,可以发现,如果能凑成"kezi",就让它凑成"kezi",其次考虑凑成"shunzi",是最优的。

比如:1w2w3w2w3w4w3w4w5w,1w1w1w2w2w2w3w3w3w的情况,考虑一下就能发现。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

const int N = 2e5 + 7;
vector<int> res[5][10];
char id[500];
int mp[500];
int cnt[5][15];
int bk[5][15];
bool v[5][15];
bool check() {
int now = 0;
for (int i = 1; i <= 7; ++ i ) {
if (cnt[4][i] && cnt[4][i] != 3)
return false;
if (cnt[4][i] == 3) {
cnt[4][i] -= 3;
now ++;
}
}
if (now == 4) return true;
for (int i = 1; i <= 3; ++ i ) {
for (int j = 1; j <= 9; ++ j ) {
if (cnt[i][j] >= 3) {
cnt[i][j] -= 3; now ++;
}
if (cnt[i][j] ) {
if (cnt[i][j+1] && cnt[i][j+2]) {
if(min(cnt[i][j+1], cnt[i][j+2]) < cnt[i][j]) return false;
int c = min({cnt[i][j], cnt[i][j+1],cnt[i][j+2]});
if (c != cnt[i][j]) return false;
now += c;
cnt[i][j] -= c; cnt[i][j+1] -= c; cnt[i][j+2] -= c;
}
if (cnt[i][j]) return false;
}
}
}
return now == 4;
}
// vector<pair<string,string> > res;
void solve() {
for (int i = 1; i <= 4; ++ i ) for (int j =1 ; j <= 9; ++ j) res[i][j].clear();
// res.clear();
memset(cnt,0,sizeof cnt);
memset(bk,0,sizeof bk);
string s;cin >> s;
for (int i = 0; i < s.size(); i += 2) {
int num = s[i] - '0';
int po = mp[s[i+1]];
cnt[po][num] ++;
}
for (int i = 1; i <= 4; ++ i ) {
for (int j = 1; j <= 9 - (i == 4) * 2; ++ j ) {
if (cnt[i][j] >= 2) {
cnt[i][j] -= 2;
memcpy(bk,cnt,sizeof bk);
if (check()) {
puts("Tsumo!");
return;
}
memcpy(cnt,bk,sizeof bk);
cnt[i][j] += 2;
}
}
}
for (int i = 1; i <= 4; ++ i) {
for (int j = 1; j <= 9 - ( i == 4 ) * 2; ++ j) {
if (cnt[i][j]) {
cnt[i][j] --;
for (int k = 1; k <= 4; ++ k) {
for (int kk = 1; kk <= 9-(k==4)*2; ++ kk) {
if (i == k && j == kk) continue;
cnt[k][kk] ++ ;
memset(v,0,sizeof v);
for (int ii = 1; ii <= 4; ++ ii) {
for ( int jj = 1; jj <= 9 - (ii == 4) * 2; ++ jj) {
if (cnt[ii][jj] >= 2) {
memcpy(bk,cnt,sizeof bk);
cnt[ii][jj] -= 2;
if (check() && !v[k][kk]) {
v[k][kk] = 1;
res[i][j].emplace_back(kk);
res[i][j].emplace_back(k);
}
memcpy(cnt,bk,sizeof bk);
}
}
}
cnt[k][kk] --;
}
}
cnt[i][j] ++;
}
}
}
int ret = 0;
for (int i = 1; i <= 4; ++ i)
for (int j = 1; j <= 9; ++ j)
if (res[i][j].size()) ++ ret;
printf("%d\n",ret);
for (int i = 1; i <= 4; ++ i)
for (int j = 1; j <= 9; ++ j) {
if(res[i][j].size()) {
printf("%d%c ",j,id[i]);
for (int k = 0; k < res[i][j].size(); k += 2) {
int num = res[i][j][k]; char op = id[res[i][j][k+1]];
printf("%d%c",num,op);
}
puts("");
}
}
}
signed main() {
int t;
cin >> t;
mp['w'] = 1; mp['b'] = 2; mp['s'] = 3; mp['z'] = 4;
id[1] = 'w', id[2] = 'b', id[3] = 's', id[4] = 'z';
while (t--) {
solve();

}
return 0;
}

L Simone and graph coloring

题意:

给出n个数的排列,对于每一对逆序对,连一条边,所有相邻点之间的颜色不能相同,输出使用最少颜色的染色方案。

做法:

逆序考虑每一个点,对于已经走过的比它小的点,取其颜色的最大值,即可得到当前数的答案。

使用线段树维护。

(队友说最长上升子序列就行,确实

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#define endl '\n'
#define inc(i,a,b) for (int i = a; i <= b; ++ i )
#define dec(i,a,b) for (int i = a; i >= b; -- i)
#define eb(x,y) emplace_back(x,y)
#define m0(a) memset(a,0,sizeof a)
#define minf(a) memset(a,0x3f,sizeof a)
#define mcf(a) memset(a,0xcf,sizeof a)
using namespace std;

typedef long long ll;
typedef double ff;
typedef pair<int,int> PII;
typedef pair<double,double> PDD;
const int N = 2e6 + 7;
int a[N],b[N];
bool v[N];
int n,m;

struct sgt{
int l,r,val;
#define ls u<<1
#define rs u<<1|1
#define l(u) tr[u].l
#define r(u) tr[u].r
#define val(u) tr[u].val
}tr[N<<1];

void pushup(int u) {
val(u) = max(val(ls), val(rs));
}

void build(int u,int l,int r) {
l(u) = l, r(u) = r;
if (l == r) {
val(u) = 0; return;
}
int mid = l + r >> 1;
build(ls,l,mid), build(rs,mid+1,r);
pushup(u);
}

void change(int u,int x,int v) {
if (l(u) == r(u)) {
val(u) = v; return;
}
int mid = l(u) + r(u) >> 1;
if (x <= mid) change(ls,x,v);
else change(rs,x,v);
pushup(u);
}

int ask(int u,int l,int r) {
if (l(u) >= l && r(u) <= r) return val(u);
int mid = l(u) + r(u) >> 1;
int ans = 0;
if (l <= mid) ans = max(ans, ask(ls,l,r));
if (r > mid) ans = max(ans,ask(rs,l,r));
return ans;
}

void solve() {
// cin >> n;
scanf("%d",&n);
inc(i,1,n) scanf("%d",a + i);
build(1,1,n);
change(1,a[n],1);
b[n] = 1;
int ans = 1;
dec(i,n-1,1) {
int x = a[i];
int y = ask(1,1,x-1);
b[i] = y + 1;
ans = max(ans,b[i]);
change(1,x,y+1);
}
printf("%d\n",ans);
inc(i,1,n) printf("%d ",b[i]); puts("");
}

signed main() {
int t;
scanf("%d",&t);
while(t--)
solve();

return 0;
}

M Stone Games

不会,也没补