1001鸡爪
题意:一个鸡爪是由 4 个部分组成,一个点与三个与该点相邻的边,三个边的另一端点被认为不在鸡爪中。
一个图上的鸡爪数是该图最多成形成几个鸡爪,使得图上每个点与边最多在一个鸡爪中(注意上文点与边是否在鸡爪中的定义)。
现在给你 n 条边,你可以使用任意个点,构造一个简单无向图(没有自环重边),要求最大化该图的鸡爪数,并输出n条边的两端点。如果有多解,请让输出的 2𝑛个数字在行优先遍历的顺序下,字典序最小。
思路:一个比较神奇的构造题,赛时过的时候以为是特判加一点点规律,赛后大概把这个规律弄懂了,首先就是当你不能组成一个鸡爪的时候肯定把多的边都变成1和新的点连的边,这样的字典序肯定是最小的,1比任何的都要优先。然后考虑给出的边足够组成鸡爪的情况,组成一个鸡爪的时候就是让1和别的点连满三条边就行,组成两个鸡爪怎么考虑呢,因为1这个点已经在一个鸡爪内了,我们肯定考虑让2去连,1和2的边已经用过了,2就算和3,4连满边也不够一个鸡爪的数量。那么我们考虑给1多加一条边,这样多了1和5这条边之后我们发现,1和2连的边就可以顺延给2这样2就满足鸡爪了。那么同理多个鸡爪的时候,其实就是在上一个情况下,让1多一条边,然后把边一路顺延给下一个点就行,直到最后一个点够边,但是我们发现我们不能一直给1去连边,不然往后延的时候有些点就会只有一条边,延下来一条仍旧不够构成鸡爪,所以我们把2和这个点和3和这个点都要连过,这样边一条条延下来才能让每个点都正好满足鸡爪的条件。具体实现可以看代码
代码
#include<iostream>
#include<string>
#include<map>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define ll long long
struct edge
{
ll a;
ll b;
};
ll cmp(edge x, edge y)
{
if (x.a != y.a)
{
return x.a < y.a;
}
else
{
return x.b < y.b;
}
}
void solve()
{
ll n;
cin >> n;
vector<edge> arr;
ll c1 = 2;
if (n / 3 == 1)
{
arr.push_back({ 1,2 });
arr.push_back({ 1,3 });
arr.push_back({ 1,4 });
c1 = 5;
}
else if (n / 3 >1)
{
arr.push_back({ 1,2 });
arr.push_back({ 1,3 });
arr.push_back({ 1,4 });
arr.push_back({ 1,5 });
arr.push_back({ 2,3 });
arr.push_back({ 2,4 });
c1 = 6;
}
ll c2 = 5;
ll c3 = 4;
for (int i = 1; i <= n / 3 - 2; i++)
{
arr.push_back({ 1,c1++ });
arr.push_back({ 2,c2++ });
arr.push_back({ 3,c3++ });
}
for (int i = 1; i <= n % 3; i++)
{
arr.push_back({ 1,c1++ });
}
sort(arr.begin(), arr.end(), cmp);
for (int i = 0; i < arr.size(); i++)
{
cout << arr[i].a << ' ' << arr[i].b << endl;
}
}
signed main()
{
ll t;
cin >> t;
while (t--)
{
solve();
}
}
1006 传奇勇士小凯
题意:给出一个无重边无自环的图,每个点有pi/15的概率胜利,失败了就要一直打一直打直到胜利为止,每天只能打一次,问最多期望能停留多久,答案输出最简分数。
思路:首先计算每个点的期望,停n天的贡献是n*(1-pi/15)的n-1次*pi/15.把1到n天的贡献加起来就是一个点的贡献。经过计算(把pi/15提取出来,然后再把后面的设置为t,整体去×一个(1-pi/15)然后相减做差就变成一个等比数列求和,把t算出来带回式子)可以得到一个点的期望就是这个点概率的倒数,然后从起始点跑一次dfs更新最大值就行,注意最后要输出一个最简分数,所以每个点的值我们也要以分数形式记录,这样最后直接上下同时除一个最大公约数就是最简分数,实现可看代码
代码
#include<iostream>
#include<string>
#include<map>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define ll long long
ll gcd(ll a, ll b)
{
if (a < b)
{
swap(a, b);
}
ll c = a % b;
while (c)
{
a = b;
b = c;
c = a % b;
}
return b;
}
struct node
{
ll son;
ll mo;
}arr[100005];
ll n;
node mx;
vector<vector<ll>> tu(n + 5);
void dfs(ll now, ll fa, node val)
{
if (fa == -1)
{
val.mo = arr[now].mo;
val.son = arr[now].son;
}
else
{
if (arr[now].mo != val.mo)
{
ll sum = arr[now].mo * val.mo;
ll x = gcd(arr[now].mo, val.mo);
ll nmo = sum / x;
ll sonn = arr[now].son * (nmo / arr[now].mo);
ll sonv = val.son * (nmo / val.mo);
ll nson = sonn + sonv;
val.mo = nmo;
val.son = nson;
}
else
{
val.son = arr[now].son + val.son;
}
if (mx.mo == 0)
{
mx.son = val.son;
mx.mo = val.mo;
}
else
{
ll sum = mx.mo * val.mo;
ll x = gcd(mx.mo, val.mo);
ll nmo = sum / x;
ll sonm = mx.son * (nmo / mx.mo);
ll sonv = val.son * (nmo / val.mo);
if (sonv > sonm)
{
mx.son = sonv;
mx.mo = nmo;
}
}
}
for (int i = 0; i < tu[now].size(); i++)
{
if (tu[now][i] != fa)
{
dfs(tu[now][i], now, val);
}
}
return;
}
void solve()
{
mx = { 0,0 };
cin >> n;
tu = vector<vector<ll>>(n + 5);
for (int i = 1; i < n; i++)
{
ll a, b;
cin >> a >> b;
tu[a].push_back(b);
tu[b].push_back(a);
}
for (int i = 1; i <= n; i++)
{
ll val;
cin >> val;
arr[i].mo = val;
arr[i].son = 15;
}
dfs(1, -1, { 0,0 });
ll p = gcd(mx.mo, mx.son);
if (p != 1)
{
mx.son /= p;
mx.mo /= p;
}
cout << mx.son << '/' << mx.mo << endl;
}
signed main()
{
ll t;
cin >> t;
while (t--)
{
solve();
}
}
1007 URL划分
题意:给出一个网址,让你提取指定的值
思路:签到题,暴力模拟即可
1010 女神的智慧
题意:把8个碎片合成一个睿智,碎平和结晶合成时,颜色相同就是那个颜色,不同就是左边的颜色,大结晶合成时候颜色相同就是那个颜色,不同就看下8个碎片里面左右颜色哪个数量更多,多的那个颜色就是睿智的颜色,相同就随机,给出8个碎片问最后是什么颜色,随机输出‘N’
思路:暴力模拟即可,记录一下每个颜色的数量方便最后一步合成判断。
1011在A里面找所有包含C的B
题意:给出一个长串a和c,再给出n个b串和b’串,问哪几个b串满足b在a内且c在b’内。
思路:分两部处理,判断c是否在b’很显然可以通过kmp算法优化,问题就是前面的b在a内如何快速处理,通过队友的教导发现可以使用ac自动机,ac自动机是一种多字符串匹配算法,可以在一篇长文a里面找到多个不同的我们要求的字符串。跟这题的前部分完全重合,具体实现可以看代码
代码
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define ll long long
const ll N = 1e5 + 10;
map<int, int> M;
ll trie[26][N] = { 0 };
ll net[N] = { 0 };
ll ne[10010] = { 0 };
ll enda[N] = { 0 };
ll cnt = 0;
string brr[N];
vector<vector<ll>> strs(N);
vector<ll> ansa;
string s1, s2;
void insert(string s, int xu)
{
ll now = 0;
for (int i = 0; i < s.size(); i++)
{
ll v = s[i] - 'a';
if (trie[v][now])
{
now = trie[v][now];
}
else
{
trie[v][now] = ++cnt;
now = cnt;
}
}
enda[now] = 1;
strs[now].push_back(xu);
return;
}
void build()
{
queue<ll> q;
for (int i = 0; i < 26; i++)
{
if (trie[i][0])
{
q.push(trie[i][0]);
}
}
while (q.size())
{
int u = q.front();
q.pop();
for (int i = 0; i < 26; i++)
{
int v = trie[i][u];
if (v) net[v] = trie[i][net[u]], q.push(v);
else trie[i][u] = trie[i][net[u]];
}
}
return;
}
void query(string a, int len)
{
for (int k = 0, i = 0; k < a.size(); k++)
{
i = trie[a[k] - 'a'][i];
for (int j = i; j && enda[j] != -1; j = net[j])
{
if (enda[j])
{
for (int z = 0; z < strs[j].size(); z++)
{
if (M[strs[j][z]])
{
ansa.push_back(strs[j][z]);
}
}
enda[j] = -1;
}
}
}
return;
}
void solve()
{
M.clear();
strs = vector<vector<ll>>(N);
ansa.clear();
memset(ne, 0, sizeof(ne));
memset(net, 0, sizeof(net));
memset(enda, 0, sizeof(enda));
memset(trie, 0, sizeof(trie));
cnt = 0;
ll n;
cin >> n;
cin >> s1 >> s2;
s2 = ' ' + s2;
int j = 0;
for (int i = 2; i < s2.size(); i++)
{
while (j && s2[i] != s2[j + 1]) j = ne[j];
if (s2[j + 1] == s2[i]) j++;
ne[i] = j;
}
for (int i = 1; i <= n; i++)
{
string b, c;
cin >> b >> c;
insert(b, i);
c = ' ' + c;
int nj = 0;
for (int j = 1; j < c.size(); j++)
{
while (nj && c[j] != s2[nj + 1]) nj = ne[nj];
if (c[j] == s2[nj + 1]) nj++;
if (nj == s2.size() - 1)
{
M[i] = 1;
break;
}
}
}
build();
query(s1, s2.size() - 1);
sort(ansa.begin(), ansa.end());
for (int i = 0; i < ansa.size(); i++)
{
cout << ansa[i] << ' ';
}
cout << endl;
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll t;
cin >> t;
while (t--)
{
solve();
}
}