基础练习1
基础练习1
HDU1214 圆桌会议
题意:
有一个\(1 \sim n\)的升序圆排列,我们每次能让相邻两个数交换位置,至少操作多少次,能使得每个数前后两个数都发生交换。
解题思路:
其实就是将一个\(1 \sim n\)的圆排列通过交换相邻元素得到\(n \sim 1\)的圆排列。
简单手玩一下,不难发现,我们总能将一个圆排列分成两部分\(1 \sim\lfloor \frac{n}{2} \rfloor ,n - \lfloor \frac{n}{2} \rfloor \sim n\)。
因为每个数总要我们将每个部分看作一个长度为\(len\)数列,那么通过交换相邻两项是的数列完全逆序需要\(\frac{len \times (len - 1)}{2}\)次操作。两部分同理。
代码:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void solve()
{
int n;
while(cin >> n)
{
if(n < 3)
{
cout << 0 << endl;
}
else
{
ll a = n / 2;
ll b = n - a;
ll ans = (a * (a - 1)) / 2 + (b * (b - 1)) / 2;
cout << ans << endl;
}
}
}
int main()
{
int t = 1;
while(t --)
{
solve();
}
return 0;
}
HDU1870 愚人节的礼物
题意:
圆括号一层套一层,字符串中一个字符B,找到B所在的层数。
解题思路:
找到左括号加一,找到右括号减一,找到B结束,答案为之前累计的数。
代码:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void solve()
{
string s;
while(cin >> s)
{
int ans = 0;
for(auto c : s)
{
if(c == '(')
{
ans ++;
}
else if(c == ')')
{
ans --;
}
else
{
cout << ans << endl;
break;
}
}
}
}
int main()
{
int t = 1;
while(t --)
{
solve();
}
return 0;
}
HDU2203 亲和串
题意:
给定字符串A,B。其中A可看做首位相接的循环字符,判断B是否是A的一个子串。
解题思路:
在A的后面加一个A。然后字符串哈希。由于B的长度固定了,所以可直接遍历\(O(1)\)判断。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int P = 131;
using ll = long long;
using ull = unsigned long long;
typedef unsigned long long ull;
ull p[N];
ull h1[N];
ull h2[N];
ull get(int l,int r)
{
return h1[r] - h1[l-1] * p[r - l + 1];
}
void solve()
{
string s1,s2;
while(cin >> s1 >> s2)
{
if(s2.size() > s1.size())
{
puts("no");
continue;
}
s1 += s1;
s1 = ' ' + s1;
s2 = ' ' + s2;
p[0] = 1;
ll n = s1.size();
for(int i = 1;i<n;i ++)
{
h1[i] = h1[i-1] * P + s1[i];
p[i] = p[i - 1] * P;
// cout << i << ' ' << endl;
}
// cout << endl;
for(int i = 1;i<s2.size();i++)
{
h2[i] = h2[i-1] * P + s2[i];
}
ll len = s2.size() - 1;
bool f = false;
for(int i = 1;i<=s1.size() - len;i++)
{
if(get(i,i + len - 1) == h2[len])
{
// cout << get(i,i + len - 1) << ' ' << h2[len] << endl;
// cout << i << endl;
f = true;
break;
}
}
if(f)
{
puts("yes");
}
else
{
puts("no");
}
}
}
int main()
{
int t = 1;
while(t --)
{
solve();
}
return 0;
}
天真愚蠢的麻瓜
题意:
给定4个二维坐标,前三个在一个圆内或者圆上,该圆尽量小。判断第4个坐标是否也在圆内。
解题思路:
覆盖三个点的最小圆,这里可枚举7中情况:
- 三点共圆 +1
- 分别以三个点为圆心 +3
- 分别以三个点连成的三条边的中点为圆心 + 3
枚举即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int P = 131;
using ll = long long;
using ull = unsigned long long;
typedef unsigned long long ull;
using pdd = pair<double,double>;
#define fi first
#define se second
int T = 0;
pdd tpc(double x1, double y1, double x2, double y2, double x3, double y3)
{
double a = 2 * (x2 - x1);
double b = 2 * (y2 - y1);
double c = x2 * x2 - x1 * x1 + y2 * y2 - y1 * y1;
double d = 2 * (x3 - x1);
double e = 2 * (y3 - y1);
double f = x3 * x3 - x1 * x1 + y3 * y3 - y1 * y1;
double g = a * e - b * d;
return {(c * e - b * f) / g,(a * f - d * c) / g};
}
double get(double x1,double y1,double x2,double y2)
{
return sqrtl((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
pdd mid(double x1,double y1,double x2,double y2)
{
double x = (x1 + x2) / 2;
double y = (y1 + y2) / 2;
return {x,y};
}
void solve()
{
vector<pdd> v(3);
cin >> v[0].fi >> v[0].se >> v[1].fi >> v[1].se >> v[2].fi >> v[2].se;
v.push_back(mid(v[0].fi,v[0].se,v[1].fi,v[1].se));
v.push_back(mid(v[0].fi,v[0].se,v[2].fi,v[2].se));
v.push_back(mid(v[2].fi,v[2].se,v[1].fi,v[1].se));
v.push_back(tpc(v[0].fi,v[0].se,v[1].fi,v[1].se,v[2].fi,v[2].se));
double a,b;
cin >> a >> b;
double mind = 1e9 + 1;
double ans = 0;
vector<double> r;
vector<double> dist;
vector<bool> st(v.size());
for(int i = 0;i < v.size();i++)
{
if(i == 0)
{
double d1 = get(v[i].fi,v[i].se,v[1].fi,v[1].se);
double d2 = get(v[i].fi,v[i].se,v[2].fi,v[2].se);
double tr = max(d1,d2);
double td = get(v[i].fi,v[i].se,a,b);
r.push_back(tr);
dist.push_back(td);
st[i] = true;
}
else if(i == 1)
{
double d1 = get(v[i].fi,v[i].se,v[0].fi,v[0].se);
double d2 = get(v[i].fi,v[i].se,v[2].fi,v[2].se);
double tr = max(d1,d2);
double td = get(v[i].fi,v[i].se,a,b);
r.push_back(tr);
dist.push_back(td);
st[i] = true;
}
else if(i == 2)
{
double d1 = get(v[i].fi,v[i].se,v[1].fi,v[1].se);
double d2 = get(v[i].fi,v[i].se,v[0].fi,v[0].se);
double tr = max(d1,d2);
double td = get(v[i].fi,v[i].se,a,b);
r.push_back(tr);
dist.push_back(td);
st[i] = true;
}
else if(i == 3)
{
double d1 = get(v[i].fi,v[i].se,v[1].fi,v[1].se);
double d2 = get(v[i].fi,v[i].se,v[2].fi,v[2].se);
if(d2 > d1)
{
st[i] = false;
}
double tr = max(d1,d2);
double td = get(v[i].fi,v[i].se,a,b);
r.push_back(tr);
dist.push_back(td);
st[i] = true;
}
else if(i == 4)
{
double d1 = get(v[i].fi,v[i].se,v[1].fi,v[1].se);
double d2 = get(v[i].fi,v[i].se,v[2].fi,v[2].se);
if(d1 > d2)
{
st[i] = false;
}
double tr = max(d1,d2);
double td = get(v[i].fi,v[i].se,a,b);
r.push_back(tr);
dist.push_back(td);
st[i] = true;
}
else if(i == 5)
{
double d1 = get(v[i].fi,v[i].se,v[0].fi,v[0].se);
double d2 = get(v[i].fi,v[i].se,v[2].fi,v[2].se);
if(d1 > d2)
{
st[i] = false;
}
double tr = max(d1,d2);
double td = get(v[i].fi,v[i].se,a,b);
r.push_back(tr);
dist.push_back(td);
st[i] = true;
}
else
{
// cout <<"nisdfsadsafdasfasdf" <<endl;
double d1 = get(v[i].fi,v[i].se,v[1].fi,v[1].se);
double d2 = get(v[i].fi,v[i].se,v[2].fi,v[2].se);
double tr = max(d1,d2);
double td = get(v[i].fi,v[i].se,a,b);
// cout << td << endl;
r.push_back(tr);
dist.push_back(td);
st[i] = true;
}
mind = min(mind,r.back());
}
// cout << mind <<endl;
for(int i = 0;i<v.size();i++)
{
// cout << i << ' ';
// cout << v[i].fi << ' ' << v[i].se;
// cout << ' ' << r[i] << ' ' << dist[i] << endl;
if(mind == r[i] && st[i])
{
if(dist[i] > r[i])
{
printf("Case #%d: Safe\n",T);
return;
}
}
}
printf("Case #%d: Danger\n",T);
}
int main()
{
int t = 1;
cin >> t;
while(t --)
{
T ++;
solve();
}
return 0;
}
HDU1285 确定比赛名次
题意:
给定n个队伍,m个二元关系,(a,b)指的是a排在b的前面。输出从前到后的排序,确保给定关系的同时小的编号在前。
解题思路:
a向b连有向边,按拓扑序就是前后关系。
要求先标编号小的,那就用优先队列记录度数为0的点。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
const int P = 131;
using ll = long long;
using ull = unsigned long long;
typedef unsigned long long ull;
using pdd = pair<double,double>;
#define fi first
#define se second
void solve()
{
int n,m;
while(cin >> n >> m)
{
vector<int> e[n + 1];
vector<int> d(n + 1,0);
for(int i = 1;i<=m;i++)
{
int a,b;
cin >> a >> b;
e[a].push_back(b);
d[b] ++;
}
priority_queue<int,vector<int>,greater<int>> q;
for(int i = 1;i<=n;i++)
{
if(!d[i])
{
q.push(i);
}
}
vector<int> ans;
while(q.size())
{
auto u = q.top();
q.pop();
ans.push_back(u);
for(auto v : e[u])
{
d[v] --;
if(d[v] == 0)
{
q.push(v);
}
}
}
for(int i = 0;i < ans.size();i++)
{
cout << ans[i];
if(i != ans.size() - 1)
{
cout << ' ';
}
else
{
cout << endl;
}
}
}
}
int main()
{
int t = 1;
while(t --)
{
solve();
}
return 0;
}
HDU1540 Tunnel Warfare
题意:
开始有个\(1 \sim n\)共n个点的直线连通块。我们根据指令进行操作:
D x: x处成断点
Q x: x所在直线连通块有多少个点
R:恢复上一个变成断点的位置
解题思路:
用栈记录断点。
对于查找,我们只要找当前点\(v\)的前面一个断点和该断点的后面一个断点就可以了。两相邻断点间的点个数就是连通块中点的个数。当然,如果自己是断点,那么返回的就是\(0\)了。
对于记录断点和查找断点我们用\(set\),\(log\)插入,\(log\)查找。
时间复杂度\(O(mlogn)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
const int P = 131;
using ll = long long;
using ull = unsigned long long;
typedef unsigned long long ull;
using pdd = pair<double,double>;
#define fi first
#define se second
void solve()
{
int n,m;
while(cin >> n >> m)
{
stack<int> q;
set<int> s;
s.insert(0);
s.insert(n + 1);
while(m --)
{
string t;
cin >> t;
if(t == "D")
{
int x;
cin >> x;
s.insert(x);
q.push(x);
}
else if(t == "R")
{
auto u = q.top();
q.pop();
s.erase(u);
}
else
{
int u;
cin >> u;
if(s.find(u) != s.end())
{
cout << 0 << endl;
}
else
{
auto l = s.upper_bound(u);
auto r = l --;
// cout << (*r) << endl;
// cout << (*l) << endl;
cout << ((*r) - (*l) - 1) << endl;
}
}
}
}
}
int main()
{
int t = 1;
while(t --)
{
solve();
}
return 0;
}