怎么说呢,签到题太签到了,后面看似是签到题的也是很考验思维...
L题:Zhang Fei Threading Needles - Thick with Fine
Solution
直接输出n-1
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int n; cin >> n;
cout << n - 1 << endl;
return 0;
}
A题:A - Drill Wood to Make Fire
Solution
语法题
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int t; cin >> t;
while (t--)
{
int n, s, v; cin >> n >> s >> v;
if (s * v >= n)cout << 1 << endl;
else cout << 0 << endl;
}
return 0;
}
I题:Tree
题意:两种操作,将一棵树两点之间的路径上的所有边权异或上一个数,或者询问一个点周围所有边权的异或和。
Solution
想了很久,最后硬做超时了,考虑这样一个事实:即两点之间的所有点(除了两个端点),必然有有且只有两条边的边权会被异或,而由于两条边a,b都经过了异或操作,所以不影响a异或b的值,
a^c^b^c=a^b^c^c=a^b,所以只需要将两个端点的边权异或和异或c即可,我们直接维护每个点的边权异或和
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 10;
vector<pii>tr[N];
int ans[N];
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int n, q; cin >> n >> q;
for (int i = 1; i <= n - 1; i++)
{
int a, b, w; cin >> a >> b >> w;
tr[a].push_back({ b,w });
tr[b].push_back({ a,w });
}
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < tr[i].size(); j++)
ans[i] ^= tr[i][j].second;
}
while (q--)
{
int op; cin >> op;
if (op == 1)
{
int x, y, w; cin >> x >> y >> w;
ans[x] ^= w;
ans[y] ^= w;
}
else
{
int x; cin >> x;
cout << ans[x] << endl;
}
}
return 0;
}
K题:Split
题意:给出一个正整数 n 和一个长度为n的单调不升序列a,然后给出一个正整数 ,表示操作总数。 操作共有两种。第一种是将 变成 (1 < x < n)。 第二种是询问操作。假设将序列分为 k段(每段长度至少为 1),每段的价值为最大的数减最小的数,求所有分段方案中最小的 k 段价值和。
Solution
注意到将序列分成K段的操作,也就是在所有数字的间隔中插入(k-1)个隔板,这里假设K==3,即在序列中选择2个地方放入隔板,假设放在和后,由于序列单调不升,所以k段价值和为
,要使得其最小,即要找到最小的两端差分,查询操作变成了查询前最小k-1段差分之和,这一点我们可以先将差分数组求出来,随后排序+前缀和,但这里还有一个修改操作,这也是让我止步的地方,想了很久没有思路,但这里注意到修改后的值不是凭空产生的的,是与左右两个数有关系的,这里注意到被改变后,其前后差分的值只不过交换了位置,不影响差分数组,所以修改操作是无效的,我们只需要关注查询操作即可
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int a[N];
int cf[N];
int sum[N];
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int n; cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
cf[i] = a[i] - a[i - 1];
}
sort(cf+1,cf+1+n);
for(int i=1;i<=n;i++)
sum[i]=cf[i]+sum[i-1];
int q; cin >> q;
while (q--)
{
int op, x; cin >> op >> x;
if(op==1)
{
cout<<a[1]-a[n]+sum[x-1]<<endl;
}
}
return 0;
}
J题:Function
题意:给出一个正整数 n 和 n 个二次项系数为 1 的二次函数,第 i 个函数形如 然后给出一个正整数 m ),表示操作总数。 操作共有两种,第一种是给出a,b,添加一个二次项系数为 1 的二次函数,形如 。第二种是询问所有二次函数在 x =a 处的最小函数值。 具体的,每次操作会先给出操作的类型,如果是 0 表示是第一种操作,如 果是 1 表示是第二种操作。对于第一种操作,会再给出两个正整数 a, b。 对于第二种操作,则会再给出一个正整数 a。
Solution
查询 x = a 时函数的最小值,对于顶点在x==a处的函数,其值为,由于,假设取最大值n,则对于x=a左右一定范围的函数,超过一定范围后,其在x=a的值一定大于n,则这些范围的函数可以不用再看,我们取极端情况,其=0时,令,得或,我们最多每次枚举的范围,时间复杂度
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
vector<int>b[N];
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int n; cin >> n;
for (int i = 1; i <= n; i++)
{
int x; cin >> x;
b[i].push_back(x);
}
int q; cin >> q;
while (q--)
{
int op; cin >> op;
if (op == 0)
{
int a, x; cin >> a >> x;
b[a].push_back(x);
}
else
{
int a; cin >> a;
int ans = 1e18;
int l = max((int)1, a - (int)sqrt(n)-1), r = min(n, a + (int)sqrt(n)+1);
for (int i = l; i <= r; i++)
{
for (int j = 0; j < b[i].size(); j++)
ans = min(ans, (a - i) * (a - i) + b[i][j]);
}
cout << ans << endl;
}
}
return 0;
}