传送门:CF
前提提要:无
A题:A. Twin Permutations
简单思维题.不难发现只要将所有数对 a i + b i ai+bi ai+bi控制等于 n n n即可.因为两个数列都是排列,所以这个是必可以满足的.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int a[maxn];
int main() {
int T=read();
while(T--) {
int n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) {
cout<<n-a[i]+1<<" ";
}
cout<<endl;
}
return 0;
}
B题:B. Array merging
简单思维题.因为我们可以掌控出数的时刻.不难发现,我们只要找到两个数列中每一个数字的连续的个数即可.我们最终的答案就是两个数列中最大的每一个数字的连续个数和.
举一个栗子,对于合并,假设我们最终的答案是
1
1
1,我们肯定是需要找到a数列中最长的连续的1的位置(记为pos1)以及b数列中最长的连续的1的位置(记为pos2),我们先将pos2,pos1之前的所有数字都加入c中,然后我们再加入所有的1即可.
注意,在一个数列中假设数字不是连续的,我们必然是不可能让它们在合并后的数列中是连续的.这个很显然,就不在赘述了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int a[maxn],b[maxn];
int main() {
int T=read();
while(T--) {
int n=read();
map<int,int>num1,num2;
for(int i=1;i<=n;i++) {
a[i]=read();
}
for(int i=1;i<=n;i++) {
b[i]=read();
}
int cnt=1;num1[a[1]]=1;
for(int i=2;i<=n;i++) {
if(a[i]==a[i-1]) {
cnt++;
}
else {
num1[a[i-1]]=max(num1[a[i-1]],cnt);
cnt=1;
}
if(i==n) {
num1[a[i]]=max(num1[a[i]],cnt);
}
}
cnt=1;num2[b[1]]=1;
for(int i=2;i<=n;i++) {
if(b[i]==b[i-1]) {
cnt++;
}
else {
num2[b[i-1]]=max(num2[b[i-1]],cnt);
cnt=1;
}
if(i==n) {
num2[b[i]]=max(num2[b[i]],cnt);
}
}
int maxx=-int_INF;
for(int i=1;i<=2*n;i++) {
maxx=max(maxx,num1[i]+num2[i]);
}
cout<<maxx<<endl;
}
return 0;
}
C题:C. Copil Copac Draws Trees
一道图论题.推一推样例之后很容易知道应该怎么做.
考虑对所有给定的边进行建树,并且记录每一条边的优先级.
然后我们就可以开始从根节点开始跑dfs,对于每一条边,假设我们的下一条边的优先级小于我们的当前边,我们就可以在一次循环中将其遍历,反之我们需要将花费+1.
但是需要注意的是,我们在一次循环中可能遇到很多条需要加花费的边,这些边的花费是可以在一次循环中解决的,也就是花费只需要一,所以我们不能反复累加.
解决方案是,可以记录遍历到每一个节点需要的花费,最后在每一个叶子结点取一个
m
a
x
max
max.
图论题不是很好用文字来讲明白,建议结合代码食用
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
map<int,map<int,int> >mp;
vector<int>tree[maxn];
int vis[maxn];int ans=0;
void dfs(int u,int per_u,int last,int dep) {
for(int i=0;i<tree[u].size();i++) {
int v=tree[u][i];
if(v==per_u) continue;
if(mp[u][v]>last) {
dfs(v,u,mp[u][v],dep);
}
else {
dfs(v,u,mp[u][v],dep+1);
}
}
ans=max(ans,dep);
}
int main() {
int T=read();
while(T--) {
int n=read();
for(int i=1;i<=n;i++) {
tree[i].clear();
vis[i]=0;
}
mp.clear();
for(int i=1;i<=n-1;i++) {
int u=read(),v=read();
tree[u].push_back(v);
tree[v].push_back(u);
mp[u][v]=mp[v][u]=i;
}
ans=1;
dfs(1,0,0,1);
cout<<ans<<endl;
}
return 0;
}
D题:D. The BOSS Can Count Pairs
本题的突破口也是比较经典的了.对于这种看起来很像 n 2 n^2 n2的题目,一般都有一些性质.比如本题,在数的值域方面就是一个突破口.很显然对于n^2的暴力算法,我们的值域可以达到1e9,但是为什么这里的值域才n呢,想到这里,对于这道题应该就有思路了.
对于
a
i
+
a
j
=
b
i
+
b
j
ai+aj=bi+bj
ai+aj=bi+bj,又因为
b
i
+
b
j
<
=
2
∗
n
bi+bj<=2*n
bi+bj<=2∗n,所以有
a
i
∗
a
j
<
=
2
∗
n
ai*aj<=2*n
ai∗aj<=2∗n,因为最终是求点对的数量,和先后顺序无关,所以我们不妨将a数组从小到大排一个序.此时我们保证
a
i
<
=
a
j
ai<=aj
ai<=aj.
然后我们枚举
a
j
aj
aj,注意此时我们不能枚举
a
i
ai
ai,这样我们的复杂度还是炸裂的(此时为
a
i
ai
ai~
2
∗
n
/
a
[
i
]
2*n/a[i]
2∗n/a[i]),至于为什么枚举
a
j
aj
aj竟然就可以缩小复杂度呢,这就是算法的巧妙所在了.真是太妙啦.
考虑枚举 a j aj aj,然后我们此时可以枚举 a i ai ai的值域,显然我们的 a i < = 2 ∗ n ai<=\sqrt{2*n} ai<=2∗n,因为此时 a i < = a j ai<=aj ai<=aj.此时我们就可以将复杂度降为 n n n\sqrt{n} nn了.因为此时枚举了 a j aj aj,所以我们也知道了 b j bj bj,所以我们就知道了与 a i ai ai相配的 b i bi bi的大小,也就是 a i ∗ a j − b j ai*aj-bj ai∗aj−bj.所以此时我们的问题就变成了有多少个 < a i , b i > <ai,bi> <ai,bi>满足这个关系.这个也不难解决,使用一个桶记录一下即可.
注意本题比较卡内存和时限,map会被卡,数组大小需要计算一下,还需要开longlong
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
int mp[635][200010];
#define int long long
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
struct Node{
int a,b;
bool operator < (const Node &rhs) const {
return a<rhs.a;
}
}node[200010];
int mp1[200010],mp2[200010];
signed main() {
int T=read();
while(T--) {
int n=read();
for(int i=1;i<=n;i++) {
node[i].a=read();
mp1[node[i].a]++;
}
for(int i=1;i<=n;i++) {
node[i].b=read();
mp2[node[i].b]++;
}
sort(node+1,node+n+1);
int ans=0;
int limit=__builtin_sqrt(2*n);
for(int i=1;i<=n;i++) {
for(int j=1;j<=limit&&j*node[i].a<=2*n;j++) {
if(mp1[j]) {
if(node[i].a*j-node[i].b>n||node[i].a*j-node[i].b<0) continue;
ans+=(ll)mp[j][node[i].a*j-node[i].b];
}
}
if(node[i].a<=limit)
mp[node[i].a][node[i].b]++;
}
for(int i=1;i<=n;i++) {
if(node[i].a<=limit)
mp[node[i].a][node[i].b]=0;
mp1[node[i].a]=0;mp2[node[i].b]=0;
}
cout<<ans<<endl;
}
return 0;
}