Pinely Round 4 (Div. 1 + Div. 2)
Preface
难得地有直觉的一场,50min 过了前 5 题后形式大好,然后 F 也一眼看出了有个斐波那契的上界
结果写了个暴力判断交上去一直挂,前面一直以为是一定有解的阈值设错了,没想到挂了好几发后发现暴力漏了一种 Case,真是唐完了
A. Maximize the Last Element
不难发现只有奇数位置上的数可以保留下来
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=105;
int t,n,a[N];
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
int mx=0; for (i=1;i<=n;i+=2) mx=max(mx,a[i]);
printf("%d\n",mx);
}
return 0;
}
B. AND Reconstruction
对于每一位分别考虑,若 \(b_i\) 该位为 \(1\),则 \(a_i\) 和 \(a_{i+1}\) 的这一位都必须是 \(1\);然后默认其余的都为 \(0\) 最后检验下是否合法即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,a[N],b[N],c[N];
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i,j; for (scanf("%d",&n),i=1;i<n;++i) scanf("%d",&b[i]);
for (i=1;i<=n;++i) a[i]=0; bool flag=1;
for (j=0;j<30&&flag;++j)
{
for (i=1;i<=n;++i) c[i]=0;
for (i=1;i<n;++i)
if ((b[i]>>j)&1) c[i]=c[i+1]=1;
for (i=1;i<n;++i)
if (!((b[i]>>j)&1)&&c[i]&&c[i+1]) { flag=0; break; }
for (i=1;i<=n;++i) if (c[i]) a[i]|=(1<<j);
}
if (!flag) { puts("-1"); continue; }
for (i=1;i<n;++i) assert((a[i]&a[i+1])==b[i]);
for (i=1;i<=n;++i) printf("%d%c",a[i]," \n"[i==n]);
}
return 0;
}
C. Absolute Zero
神秘构造题,看完题面就感觉是个那一堆 \(2\) 的幂次来构造,还在想怎么会有无解的情况
后面仔细一想发现如果初始序列中又有奇数又有偶数,则每次操作后序列仍然是有奇有偶的,因此永远不可能全为 \(0\)
否则手玩一波后会发现对于偶数就用形如 \(2^{29},2^{28},\dots,2^1,1,1\) 这样的构造方式;奇数就用形如 \(2^{29},2^{28},\dots,2^1,1\) 这样的构造方式即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int t,n,a[N];
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; int c[2]={0,0};
for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]),++c[a[i]&1];
if (c[0]>0&&c[1]>0) { puts("-1"); continue; }
if (c[0]>0)
{
puts("31");
for (i=29;i>=0;--i) printf("%d ",1<<i);
puts("1");
} else
{
puts("30");
for (i=29;i>=0;--i) printf("%d%c",1<<i," \n"[i==0]);
}
}
return 0;
}
D. Prime XOR Coloring
很需要直觉的一个题,不然可能想半天想不出来
由于自然数中最小的不是质数的数是 \(4\),因此考虑让同色的数之间的异或值都是 \(4\) 的倍数就可以构造一种符号要求的做法
显然将所有数按照它们模 \(4\) 的余数分组,同组数染一种颜色即可,这样组内任意两数异或值都是 \(4\) 的倍数
最后对于数比较小的情况,把样例抄下来就完事
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int t,n;
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
scanf("%d",&n);
if (n==1) puts("1\n1"); else
if (n==2) puts("2\n1 2"); else
if (n==3) puts("2\n1 2 2"); else
if (n==4) puts("3\n1 2 2 3"); else
if (n==5) puts("3\n1 2 2 3 3"); else
{
puts("4");
for (RI i=1;i<=n;++i) printf("%d%c",i%4+1," \n"[i==n]);
}
}
return 0;
}
E. Coloring Game
思路很简单的一个题,感觉只有正常的 Div.2 D 的难度
首先考虑来个黑白染色,如果给出的图不是二分图那么选先手,保证每次都给 1 2
两种颜色,此时一定必胜
否则如果给出的图是二分图就选后手,不妨设原图中的黑点对应颜色 \(1\),白点对应颜色 \(2\)
对于交互器每次给出的两种颜色,如果存在颜色 \(1/2\) 且它们对应的点还没有被染色,那么就直接对应地染色
否则如果不存在上述情况,则必然存在一种颜色已经把对应的点涂完了,那剩下没涂完的点都用颜色 \(3\) 即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=10005;
int t,n,m,x,y,col[N]; bool flag; vector <int> v[N],c[2];
inline void DFS(CI now)
{
for (auto to:v[now])
if (col[to]==-1) col[to]=col[now]^1,DFS(to);
else if (col[to]==col[now]) flag=0;
}
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; scanf("%d%d",&n,&m);
for (i=1;i<=n;++i) v[i].clear(),col[i]=-1;
for (i=1;i<=m;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
flag=1; c[0].clear(); c[1].clear();
for (i=1;i<=n;++i) if (col[i]==-1) col[i]=0,DFS(i);
if (!flag)
{
printf("Alice\n"); fflush(stdout);
for (i=1;i<=n;++i)
{
printf("1 2\n"); fflush(stdout);
int x,y; scanf("%d%d",&x,&y);
}
} else
{
printf("Bob\n"); fflush(stdout);
for (i=1;i<=n;++i) c[col[i]].push_back(i);
for (i=1;i<=n;++i)
{
int x,y; scanf("%d%d",&x,&y);
if ((x==1||y==1)&&!c[0].empty())
{
printf("%d 1\n",c[0].back());
c[0].pop_back(); fflush(stdout); continue;
}
if ((x==2||y==2)&&!c[1].empty())
{
printf("%d 2\n",c[1].back());
c[1].pop_back(); fflush(stdout); continue;
}
if (!c[0].empty())
{
printf("%d 3\n",c[0].back());
c[0].pop_back(); fflush(stdout);
} else
{
printf("%d 3\n",c[1].back());
c[1].pop_back(); fflush(stdout);
}
}
}
}
return 0;
}
F. Triangle Formation
神秘观察题,发现一定有解的下界后就很简单了
考虑如果在一堆棍子里找一个三角形,那么排序之后找相邻的三个一定最优
因此如果要构造一个三角形都找不到的数据,则木棍的长度一定形如斐波那契数列
简单计算发现第 \(44\) 个斐波那契数大于 \(10^9\),然后就说明当区间长度 \(>50\) 时一定有解
现在考虑怎样快速判断能否找到两个三角形,不妨先给所有数从小到大排序,并钦定第一个三角形的最长边小于等于第二个三角形的最长边
枚举第一个三角形的最长边,分以下三种情况讨论:
- 第二个三角形三条边都在当前这条边后面,这个可以预处理每个后缀的信息
- 第二个三角形较长的两条边都在当前这条边后面,这个只需要求出后缀中差值最小的两条边,然后每次在前面找一个是否大于这个差值的边
- 第二个三角形最长的一条边在当前这条边后面,此时需要找到前面还没被用的最长的两条边,和之后的最短的边比较下即可
那么我们再枚举第一个三角形的次长边,然后用一个 two pointers
扫一下即可,单组询问复杂度 \(\min^2(50,r-l+1)\)
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005,INF=1e9;
int n,q,a[N],l,r;
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (scanf("%d%d",&n,&q),i=1;i<=n;++i) scanf("%d",&a[i]);
while (q--)
{
scanf("%d%d",&l,&r);
if (r-l+1>50) { puts("YES"); continue; }
vector <int> vec;
for (i=l;i<=r;++i) vec.push_back(a[i]);
sort(vec.begin(),vec.end());
vector <int> suf(vec.size(),0),d(vec.size(),INF);
int mnp=INF,mxp=-INF;
for (i=0;i+2<vec.size();++i)
if (vec[i]+vec[i+1]>vec[i+2])
suf[i]=1,mnp=min(mnp,i),mxp=max(mxp,i);
if (mxp-mnp>=3) { puts("YES"); continue; }
for (i=vec.size()-1;i>=1;--i) suf[i-1]|=suf[i];
for (i=vec.size()-2;i>=0;--i)
d[i]=min(d[i+1],vec[i+1]-vec[i]);
bool flag=0;
for (i=2;i+1<vec.size()&&!flag;++i)
{
for (RI j=i-1,k=0;k<j;--j)
{
while (k<j&&vec[k]+vec[j]<=vec[i]) ++k;
if (k>=j) continue;
if (suf[i+1]) { flag=1; break; }
int mxp=i-1;
while (mxp==j||mxp==k) --mxp;
if (mxp>=0&&vec[mxp]>d[i+1]) { flag=1; break; }
if (mxp<=0) continue;
int smxp=mxp-1;
while (smxp==j||smxp==k) --smxp;
if (smxp>=0&&vec[smxp]+vec[mxp]>vec[i+1]) { flag=1; break; }
}
}
puts(flag?"YES":"NO");
}
return 0;
}
Postscript
后面的题银牌✌比赛时快 2h 都没做出来,说明肯定不是我能做的了,直接白兰