【21 ZR联赛集训 day18】游戏
给定长度为 \(n\) 的序列 \(A,B\),每个数形如 \(\frac{2^{p_1}3^{p_2}5^{p_3}7^{p_4}11^{p_5}13^{p_6}}{2^{p_7}3^{p_8}5^{p_9}7^{p_{10}}11^{p_{11}}13^{p_{12}}}\)。
可以进行若干次操作,每次操作选定 \(i(2\le i\le n-1),(a_{i-1},a_i,a_{i+1})\gets (a_{i-1}a_i,\frac{1}{a_i},a_ia_{i+1})\)。
问能否把 \(A\) 变成 \(B\)。\(1\le n \le 2\times 10^5\)。
正解
首先显然我们只需要对每个数记录 \(6\) 个 \(p\) 即可,因为底数一样相除可以直接指数相减。
考虑到每次操作只和乘除有关,我们对 \(A,B\) 做前缀积数组 \(A',B'\)。可以发现每次操作其实是交换 \(a'_{i-1},a'_i\)。
那么问题就变成了做若干次相邻数字的交换,能否把 \(A\) 变成 \(B\),注意 \(a'_n\) 是不会换的。类似于冒泡排序,前 \(n-1\) 个数的所有排列我们都可以取得,因此只需要判断 \(a_n=b_n,\{A\}=\{B\}\) 即可。
对前 \(n-1\) 项排序并比较即可,时间复杂度 \(O(n\log n+nV)(V=6)\)。
手模出奇迹
赛场上我没有往前缀积这里想,自然地,幂的乘除法不好做,因为值域太大了,我们可以想到转化成指数的加减法。(然后其实可以变成交换前缀和)
但是我比较菜,想不到前缀和。首先很容易发现进行若干次操作后每个底数的所有数字的指数之和一定不变,可以判掉部分无解情况。我们又发现,若干次操作后,\(a_i\) 一定是原数组几个数相加减得到。但这还不够,于是我手模了一些小样例,具体来讲,我模拟了 \(n=4\) 的所有情况,由于 \(n=5\) 情况太多,我随机模拟了一些,发现若干次操作后,新序列每个位置一定是连续个 \(a_i\) 相加或相减,想象成一个栈里一会加入几个 \(a_i\),一会减去几个,栈内元素下标一定有序递增 \(1\),且最后栈内有 \(n\) 个元素。
考虑 DP,\(dp_{i,0/1,0/1}\) 表示第 \(i\) 个数 \(=b_i\),选择加一些 \(a_i\) 还是减一些 \(a_i\),答案尽量大还是尽量小,的答案(当前栈内元素数量)。
想要知道栈内有多少个元素,还是要前缀和,对前缀和二分找出 \(a'_x=b'_i\)。相信聪明的你已经发现这个做法和正解本质上是一致的,只是我不聪明,其实手模 dp 后就可以发现它是一个排列,本质上就是前缀和的集合相等。
Code
#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
using namespace std;
typedef long long ll;
const int N=2e5+7,inf=0x3f3f3f3f;
int n;
int x;
struct node{
int p[10];
bool operator == (const node b)const{
rep(i,1,6) if(p[i]!=b.p[i]) return 0;
return 1;
}
bool operator != (const node b) const{
rep(i,1,6) if(p[i]!=b.p[i]) return 1;
return 0;
}
bool operator < (const node b) const{
rep(i,1,6) if(p[i]!=b.p[i]) return p[i]<b.p[i];
return 0;
}
void add(node b){
rep(i,1,6) p[i]+=b.p[i];
}
}sa,sb;
struct node2{
node x[N];
bool operator ==(const node2 b)const{
rep(i,1,n){
if(x[i]!=b.x[i]) return 0;
}
return 1;
}
}a,b,c,aa;
struct node3{
node x;
int id;
bool operator <(const node3 b) const{
if(x!=b.x) return x<b.x;
else return id<b.id;
}
}Al[N];
queue<node2> que;
int dp[N][2][2];
int findmax(int l,int r,node x){
auto it=upper_bound(Al+1,Al+n+1,(node3){x,r});
it--;
node3 y=*it;
if(x==y.x&&y.id>=l&&y.id<=r) return it-Al;
else return -1;
}
int findmin(int l,int r,node x){
auto it=lower_bound(Al+1,Al+n+1,(node3){x,r});
it--;
node3 y=*it;
if(x==y.x&&y.id>=l&&y.id<=r) return it-Al;
else return inf;
}
//#define DEBUG
int main(){
#ifdef DEBUG
freopen("in.txt","r",stdin);
freopen("my.out","w",stdout);
#endif
sf("%d",&n);
rep(i,1,n){
rep(j,1,6){
sf("%d",&x);
a.x[i].p[j]+=x;
}
rep(j,1,6){
sf("%d",&x);
a.x[i].p[j]-=x;
sa.p[j]+=a.x[i].p[j];
}
}
rep(i,1,n){
rep(j,1,6){
sf("%d",&x);
b.x[i].p[j]+=x;
}
rep(j,1,6){
sf("%d",&x);
b.x[i].p[j]-=x;
sb.p[j]+=b.x[i].p[j];
}
}
aa=a;
c=b;
rep(i,2,n){
aa.x[i].add(aa.x[i-1]);
c.x[i].add(c.x[i-1]);
}
rep(i,1,n) Al[i]={aa.x[i],i};
sort(Al+1,Al+n+1);
if(sa!=sb) {
pf("No\n");
return 0;
}
dp[1][0][0]=findmax(1,n,c.x[1]);
dp[1][0][1]=findmin(1,n,c.x[1]);
dp[1][1][0]=-1;
dp[1][1][1]=inf;
rep(i,2,n){
dp[i][0][0]=max(findmax(dp[i-1][0][1],n,c.x[i]),findmax(dp[i-1][1][1],n,c.x[i]));
dp[i][0][1]=min(findmin(dp[i-1][0][1],n,c.x[i]),findmin(dp[i-1][1][1],n,c.x[i]));
dp[i][1][0]=max(findmax(1,dp[i-1][0][0],c.x[i]),findmax(1,dp[i-1][1][0],c.x[i]));
dp[i][1][1]=min(findmin(1,dp[i-1][0][0],c.x[i]),findmin(1,dp[i-1][1][0],c.x[i]));
}
auto it=lower_bound(Al+1,Al+n+1,(node3){aa.x[n],n});
int xx=it-Al;
if(dp[n][0][0]==xx) pf("Yes\n");
else pf("No\n");
}
标签:手模,le,21,int,若干次,栈内,day18,ZR,前缀
From: https://www.cnblogs.com/liyixin0514/p/18435589