cf vp
1112 div1
\(C\) 忘记了用连通块刻画连续段,没想到点边容斥。
\(D\) 的构造比较神秘。其中 \([0,L-1]\) 的异或稳定子构成的集合是 \([0,\lowbit(L)-1]\)。
\(F\) 中 \(i \in [0,2^x-1],popcount_k(i)\) 构成的字符串具有倍增的递推关系。
A. Rank Subsequence
给定长度为 \(n\) 的序列 \(a\)。其中第 \(i\) 个元素在子序列中时,满足子序列中在其之前的元素个数不属于 \([l_i,r_i]\),子序列中在其之后的元素个数不属于 \([u_i,v_i]\)。求最长子序列长度。
\(1 \le \sum n \le 5 \times 10^3,2s,256MB\)。
1
定义 \(f_{i,j}\) 表示考虑前 \(i\) 个元素子序列长度为 \(j\) 是否可行。
发现无法转移,因为不知道后面有多少元素。所以从大到小枚举子序列长度 \(len\),然后再 \(dp\)。
时间复杂度 \(O(n^3)\)。
2
套路地,考虑转 \(01\) 为最值。具体地,注意到 \(f_i\) 中有值部分是一个前缀,所以改变状态,定义 \(f_i\) 表示有值前缀的右端点。状态优化成 \(O(n)\)。
注意到只有 \(f_i\) 处的转移有效,所以转移也优化成 \(O(n)\)。
时间复杂度 \(O(n^2)\)。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 5e3+10;
static int n,l[N],r[N],u[N],v[N];
inline bool check(int mid){
int now = 0;
FOR(i,1,n){
if(!(l[i]<=now+1&&now+1<=r[i])&&!(u[i]<=mid-now&&mid-now<=v[i]))++now;
}
return mid<=now;
}
inline void solve(){
//rd
readx(n);
FOR(i,1,n)readx(l[i]),readx(r[i]),readx(u[i]),readx(v[i]);
//get ans
REP(i,n,1)if(check(i))return output(i),putchar(10),void();
output(0),putchar(10);
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
B. Permutation Cuts
对一个 \(n\) 阶排列 \(p\),\(\forall i \in [1,n-1],a_i = \min(\max_{j \le i}\{p_j\},\max_{j \ge i+1}\{p_j\})\)。
现在给定 \(a\),求有多少种合法的 \(n\) 阶排列 \(p\)。
\(2 \le \sum n \le 10^6,4s,256MB\)。
1
记 \(pm_i = \max_{j \le i}\{p_j\},sm_i = \max_{j \ge i}\{p_j\}\)
考虑 \(a_i\) 对 \(p\) 的限制。有 \(sm_{i+1} \gt pm_i = a_i\) 或者 \(\pm_i \gt sm_{i+1} = a_i\)。
因此,\(a_i = n\) 一定是不合法的。剩下的 \(a_i\) 应当构成一个单峰,并且值相同的 \(a_i\) 构成一段连续段。
记 \(a_i\) 中的最大值占据的连续段为 \([L,R]\),则 \(n\) 只能放在 \(R+1\) 或者 \(L\)。
枚举这两种情况,然后从大到小枚举值计算其放置方案数之积即可。
精细实现可以做到 \(O(n)\)。代码是 \(O(n \log n)\) 的。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <queue>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 1e6+10,mo = 998244353;
static int n,a[N],l[N],r[N],v[N],tp;
static bool vis[N];
static int id[N];
inline int cal(int pos){
std::priority_queue<int> ql,qr;
ql.push(0),qr.push(-(n+1));
FOR(i,1,tp){
if(r[i]<pos)ql.push(r[i]);
else if(l[i]>=pos)qr.push(-(l[i]+1));
else return 0;
}
FOR(i,1,n)id[i] = -1;
FOR(i,1,tp)id[v[i]] = i;
int ans = 1;
REP(i,n-1,1){
int t;
if((t=id[i])==-1){
int l = ql.top(),r = -qr.top();
++l,--r;
ans = 1ll*ans*std::max(0,(r-l+1-(n-i)))%mo;
}
else{
if(r[t]<pos){
ql.pop();
int tl = ql.top();
ans = 1ll*ans*std::max(0,l[t]-tl)%mo;
}
else{
qr.pop();
int tr = -qr.top();
ans = 1ll*ans*std::max(0,tr-(r[t]+1))%mo;
}
}
if(!ans)return 0;
}
return ans;
}
inline void solve(){
readx(n);
FOR(i,1,n)vis[i] = 0;
tp = 0;
FOR(i,1,n-1)readx(a[i]);
a[n] = -1;
FOR(i,2,n)if(a[i-1]!=a[i]){
int tv = a[i-1];
if(vis[tv]||tv==n)return output(0),putchar(10),void();
vis[tv] = 1;
int j = i-1;
for(;a[j]==tv;--j);
l[++tp] = j+1,r[tp] = i-1;
v[tp] = tv;
}
v[tp+1] = v[0] = 0;
l[tp+1] = n;
r[0] = 0;
int mx = -1,id = -1;
FOR(i,1,tp)if(mx<v[i])mx = v[i],id = i;
if(id==-1)puts("id_err"),exit(0);
FOR(i,1,id-1)if(v[i]>v[i+1])return output(0),putchar(10),void();
FOR(i,id+1,tp)if(v[i]>v[i-1])return output(0),putchar(10),void();
int ans = 0;
ans = (ans+1ll*cal(l[id])*(l[id]-r[id-1]))%mo;
ans = (ans+1ll*cal(r[id]+1)*(l[id+1]-r[id]))%mo;
output(ans),putchar(10);
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
C. Double-Rift Dial
计算 \(n\) 阶排列 \(p\) 有多少个循环移位满足所有前缀都满足从小到大排序后只有至多两个连续段。
\(1 \le \sum n \le 2 \times 10^5,2s,256MB\)。
1
先断环成链,变成对区间计算答案。
当时就觉得这个至多 \(2\) 很有用,因此一直想着一些神秘的 adhoc 做法。
实际上不如强化限制,比如至多有 \(k\) 个连续段怎么做。
这样求我们在从右往左扫描左端点的过程中,实时维护每个右端点的连续段数。
考虑将连续段数刻画成连通块数,因此考虑点边容斥,用支持区间加+区间最小值的线段树维护连通块数即可。
时间复杂度 \(O(n \log n)\)。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
static char stkk[200];
const int N = 2e5+10;
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
struct D0{
#define ls (x<<1)
#define rs (x<<1|1)
#define mid ((l+r)>>1)
int mx[N<<3],ad[N<<3];
inline void pushup(int x){
mx[x] = std::max(mx[ls],mx[rs])+ad[x];
}
inline void add_ad(int x,int d){
mx[x]+=d,ad[x]+=d;
}
void bd(int x,int l,int r){
mx[x] = ad[x] = 0;
if(l==r)return;
bd(ls,l,mid),bd(rs,mid+1,r);
}
void my(int x,int l,int r,int pl,int pr,int d){
// if(x==1)printf("x:%d pl:%d pr:%d d:%d\n",x,pl,pr,d);
if(r<pl||l>pr)return;
if(pl<=l&&r<=pr)return add_ad(x,d),void();
my(ls,l,mid,pl,pr,d),my(rs,mid+1,r,pl,pr,d);
pushup(x);
}
int qy(int x,int l,int r,int pl,int pr){
if(r<pl||l>pr)return 0;
if(pl<=l&&r<=pr)return mx[x];
return std::max(qy(ls,l,mid,pl,pr),qy(rs,mid+1,r,pl,pr))+ad[x];
}
#undef ls
#undef rs
#undef mid
}t0;
static int a[N<<1],n;
static int la[N];
inline void solve(){
//rd
readx(n);
FOR(i,1,n)readx(a[i]),a[i+n] = a[i];
//solve
t0.bd(1,1,2*n);
FOR(i,0,n+1)la[i] = 2*n+1;
int ans = 0;
// FOR(i,1,2*n)printf("%d ",a[i]);putchar(10);
REP(i,2*n,1){
int p = la[a[i]-1];
if(p!=2*n+1)t0.my(1,1,2*n,p,2*n,-1);
p = la[a[i]+1];
if(p!=2*n+1)t0.my(1,1,2*n,p,2*n,-1);
t0.my(1,1,2*n,i,2*n,1);
la[a[i]] = i;
if(i<=n){
// printf("i:%d mx:%d\n",i,t0.qy(1,1,2*n,i,i+n-1));
if(t0.qy(1,1,2*n,i,i+n-1)<=2)++ans;
}
}
output(ans),putchar(10);
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
D. Xor Permutation Matrix
要求输出一个 \(n \times n\) 的矩形,满足每行每列构成一个 \(n\) 阶排列(\(0 index\)),且每个 \(2 \times 2\) 的子矩形的异或和均为 \(x\)。
\(1 \le \sum n \le 2500,2s,512MB\)。
1
先判断一下无解。
当 \(n=2\) 时,只有 \(x=0\) 有解。
接着手玩一下。感觉这个排列的限制是比较严的。为了在构造的过程中恰好出现一个排列,感觉上 \([0,n-1]\) 要具有较好的封闭性,结合样例猜一手当 \(n = 2^x\) 时有解。
考虑浅证一下。不难发现对于相邻两行 \(i,i+1\),如果定义 \(d_j = a_{i,j} \oplus a_{i+1,j}\),则奇数列的 \(d_j\) 和偶数列的 \(d_j\) 均相同,并且二者异或和恰为 \(x\)。
考虑抵消掉 \(d_{j+1} = d_j \oplus x\)。不妨考察 \(i,i+2\) 两行,定义 \(d_j = a_{i,j} \oplus a_{i+2,j}\)。发现所有 \(d_j\) 均相等。
进一步的推广,可以得到与 \(i\) 奇偶性相同的行都具有如上性质。
相当于记集合 \(S = \{x|0 \le x \le n-1,x \in \Z\}\)。要求 \(T = \{x|x = t \oplus d_i,t \in S\}\) 恰好等于 \(S\)。并且可以找到 \(\lfloor \dfrac{n}{2} \rfloor\) 个互不相同的 \(d_i\)(因为要求列也构成排列)。
这时有结论:对集合 \(S = \{x|0 \le x \le L-1\}\) ,其所有异或稳定子构成的集合 \(T = \{x|0 \le x \le \lowbit(L)-1\}\)。可以感性理解一下。
因此 \(n = 2^k\) 是有解的必要条件。并且在此基础上易得 \(x \le n-1\) 也是有解的必要条件。
2
猜测剩下的情况都是有解的。考虑构造性证明。
当 \(x=0\) 时,注意到此时所有可用数都是异或稳定子,所以不妨大胆定义 \(a_{i,j} = i \oplus j\)。这样一行相当于是一个 \([1 \cdots n]\) 的排列异或上 \(i\),仍然是排列。容易验证其满足题目所有要求。
剩下情况,考虑大胆引入 \(x\)。比如不妨定义 \(a_{i,j} = i \oplus j \oplus ((i \bmod 2)\cdot(i \bmod 2)\cdot x)\)。容易验证这可以满足子矩形的异或要求,但不能满足排列的要求,原因是引入了 \(x\)。
注意到我们令 \(a_{i,j} = p_i \oplus p_j \oplus ((i \bmod 2)\cdot(i \bmod 2)\cdot x)\),其中 \(p\) 是排列,本质相同。
所以我们现在希望 \(p\) 的所有奇数位构成的集合的一个异或稳定子是 \(x\)。而注意到 \(x\) 是原集合的异或稳定子,所有我们可以将所有 \(y,y \oplus x\) 两两配对。然后取出一半的对放在奇数位,剩下放在偶数位就做完了。
时间复杂度 \(O(n^2)\)。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 1e5+10;
static int n,x,p[N];
static bool vis[N];
struct node{
int x,y;
}stk[N];
inline void solve(){
readx(n),readx(x);
if(n!=(1<<(int)std::__lg(n))||(n==2&&x!=0)||(x>=n))return output(-1),putchar(10),void();
if(x==0){
FOR(i,0,n-1){FOR(j,0,n-1)output(i^j),putchar(' ');putchar(10);}
return;
}
FOR(i,0,n-1)vis[i] = 0;
int tp = 0;
FOR(i,0,n-1){
if(!vis[i]){
vis[i] = vis[i^x] = 1;
stk[++tp] = (node){i,i^x};
}
}
int ttp = 0;
FOR(i,1,tp/2)p[++ttp] = stk[i].x,++ttp,p[++ttp] = stk[i].y,++ttp;
ttp = 1;
FOR(i,tp/2+1,tp)p[++ttp] = stk[i].x,++ttp,p[++ttp] = stk[i].y,++ttp;
FOR(i,0,n-1){FOR(j,0,n-1)output(p[i+1]^p[j+1]^(((i&1)&&(j&1))*x)),putchar(' ');putchar(10);}
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
E1. String (Easy Version)
对一个 \(k\) 进制数 \(x\) 定义 \(p_k(x)\) 表示各位数之和膜 \(k\) 的值。
\(q\) 次询问 \(\forall i \in [l,r]\),\(p_k(i)\) 顺次拼接构成的文本串 \(S\) 中,模式串 \(T\) 的出现次数。
\(2 \le k \le 10,1 \le q \le 10^3,1 \le \sum |T| \le 2 \times 10^6,1 \le l \le r \le 10^{17}\)。
1
定义 \(ts_{j,i}\) 表示 \([0,k^i-1]\) 内的数顺次拼接,并且整体加 \(j\) 后构成的字符串。
首先可以将 \([l,r]\) 拆成 \(O(k \log_{k}{V})\) 个 \(ts_{j,i}\)。
考虑如何合并这些 \(ts_{j,i}\),发现我们只关心内部 \(T\) 出现次数,至多长 \(n-1\) 的前/后缀,合并直接暴力跑 \(kmp\) 即可。
考虑如何递推 \(ts\),其中 \(ts_{j,0}\) 是好做的。注意到 \(ts_{j,i}\) 等于 \(ts_{j,i-1}\) 依次平移 \(0,1,\cdots,k-1\) 后的拼接的结果。
时间复杂度 \(O(k^2\log_{k}{V}\sum |T|)\)。无法通过。
2
记 \(mx\) 表示第一个 \(k^j \ge n-1\) 的 \(j\)。注意到 \(\forall i \ge mx\),\(ts_{j,i}\) 的前后缀都是某个 \(ts_{j',mx}\),而只有 \(k^2\) 个本质不同的合并结果,可以预处理。
如果预分配内存存储前后缀,对 \(i \ge mx\) \(O(1)\) 赋值的话,预处理的复杂度变成 \(O(k^2\sum |T|)\)。
而询问直接暴力合并,复杂度 \(O(k\log_{k}{V}\sum |T|)\)。
总时间复杂度 \(O(k(\log_{k}{V}+k)\sum |T|)\)。
代码由 gemini 3.1 pro 卡常
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <cstring>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ll long long
// --- 极致 Fast IO ---
namespace IO {
const int BUF_SIZE = 1 << 21;
char buf[BUF_SIZE], *p1 = buf, *p2 = buf;
inline char gc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, BUF_SIZE, stdin), p1 == p2) ? EOF : *p1++;
}
char out_buf[BUF_SIZE], *out_p = out_buf;
inline void flush() {
fwrite(out_buf, 1, out_p - out_buf, stdout);
out_p = out_buf;
}
inline void pc(char c) {
if (out_p - out_buf == BUF_SIZE) flush();
*out_p++ = c;
}
template<typename T> inline void readx(T &x) {
x = 0; int y = 1; char c = gc();
for (; c < 48 || c > 58; c = gc()) if (c == '-') y = -1;
for (; c >= 48 && c <= 58; c = gc()) x = (x << 1) + (x << 3) + (c ^ 48);
x *= y;
}
static char stkk[200];
template<typename T> inline void output(T x) {
if (!x) return pc('0'), void();
if (x < 0) x = ~x + 1, pc('-');
int top = 0;
for (; x; stkk[++top] = x % 10 ^ 48, x /= 10);
for (; top; pc(stkk[top--]));
}
struct IO_Flusher { ~IO_Flusher() { flush(); } } flusher;
}
using IO::readx;
using IO::output;
using IO::pc;
const int N = 2e6+10, K = 10, M = 64;
static int k, q, n;
static char s[N];
static int kmp[N];
static int f[K][K];
// --- 内存池 ---
char pool[500000000]; // 150MB 内存池,足够单次询问使用
char* ptop = pool;
struct MyVec {
char* ch;
int len, j;
void clear() {
ch = nullptr;
len = j = 0;
}
};
inline int cal(const MyVec &s_vec, const MyVec &p_vec) {
if (s_vec.len + p_vec.len < n || n == 1) return 0;
int j = 0, ans = 0;
REP(i, s_vec.len - 1, 0) {
char c = s_vec.ch[i];
while (j && s[j + 1] != c) j = kmp[j];
if (s[j + 1] == c) ++j;
if (j == n) ++ans, j = kmp[j];
}
FOR(i, 0, p_vec.len - 1) {
char c = p_vec.ch[i];
while (j && s[j + 1] != c) j = kmp[j];
if (s[j + 1] == c) ++j;
if (j == n) ++ans, j = kmp[j];
}
return ans;
}
inline void adx(int &x, int y) { if ((x += y) >= k) x -= k; }
struct node {
MyVec p, s_nd;
bool t;
ll su;
void init() {
p.clear(); s_nd.clear();
t = su = 0;
}
int get(const node& A) const {
return cal(s_nd, A.p);
}
node& operator+=(const node& A) {
if (t && A.t) {
su += A.su + f[s_nd.j][A.p.j];
s_nd = A.s_nd; // O(1) 浅拷贝
} else {
su += A.su + get(A);
// 处理前缀 p
int add_p = std::min(A.p.len, n - 1 - p.len);
if (add_p > 0) {
char* nch = ptop; ptop += p.len + add_p;
if (p.len > 0) memcpy(nch, p.ch, p.len);
memcpy(nch + p.len, A.p.ch, add_p);
p.ch = nch;
p.len += add_p;
}
// 处理后缀 s_nd
int add_s = std::min(s_nd.len, n - 1 - A.s_nd.len);
if (add_s > 0) {
char* nch = ptop; ptop += A.s_nd.len + add_s;
if (A.s_nd.len > 0) memcpy(nch, A.s_nd.ch, A.s_nd.len);
memcpy(nch + A.s_nd.len, s_nd.ch, add_s);
s_nd.ch = nch;
s_nd.len = A.s_nd.len + add_s;
} else {
s_nd.ch = A.s_nd.ch; // O(1) 浅拷贝
s_nd.len = A.s_nd.len;
}
s_nd.j = A.s_nd.j;
t = A.t;
}
return *this;
}
node operator+(const node& A) const {
node c = *this;
return (c += A);
}
};
static node ts[K][M], stk[M << 2];
static int tp;
static ll pw[M];
static int tl[M], tpl, tr[M], tpr;
static int gs[M];
signed main() {
readx(k), readx(q);
pw[0] = 1;
int gmx = 0;
FOR(i, 1, M - 1) {
if ((pw[i] = 1ll * pw[i - 1] * k) >= 1e17) { gmx = i; break; }
}
ll l, r;
FOR(query_idx, 1, q) {
ptop = pool; // 每次询问清空内存池,O(1) 回收所有内存
readx(l), readx(r), readx(n);
char c = IO::gc();
while(c <= 32) c = IO::gc();
FOR(idx, 1, n) {
s[idx] = c;
c = IO::gc();
}
FOR(j, 0, n + 1) kmp[j] = 0;
FOR(j, 1, n) s[j] ^= 48;
s[n + 1] = -1;
int j = 0;
FOR(idx, 2, n) {
for (; j && s[j + 1] != s[idx]; j = kmp[j]);
if (s[j + 1] == s[idx]) ++j;
kmp[idx] = j;
}
if (l > r) { output(0), pc(10); continue; }
if (l == r) {
if (n == 1) {
int now = 0;
for (; l; adx(now, l % k), l /= k);
output(now == s[1]), pc(10);
} else output(0), pc(10);
continue;
}
tpl = 0; for (; l; tl[++tpl] = l % k, l /= k);
tpr = 0; for (; r; tr[++tpr] = r % k, r /= k);
FOR(idx, tpl + 1, tpr) tl[idx] = 0;
int mx = -1;
FOR(idx, 0, gmx) if (pw[idx] >= n - 1) { mx = idx; break; }
if (mx == -1) exit(0);
FOR(j, 0, k - 1) FOR(idx, 0, M - 1) ts[j][idx].init();
FOR(j, 0, k - 1) {
node &nd = ts[j][0];
if (n == 1) nd.su = j == s[1];
nd.p.ch = ptop; *ptop++ = j; nd.p.len = 1;
nd.s_nd.ch = ptop; *ptop++ = j; nd.s_nd.len = 1;
}
FOR(idx, 1, mx) {
FOR(j, 0, k - 1) {
int tx = j;
node &nd = ts[j][idx];
nd = ts[tx][idx - 1]; // O(1) 浅拷贝
FOR(step, 1, k - 1) {
adx(tx, 1);
nd += ts[tx][idx - 1];
}
}
}
FOR(idx, 0, k - 1) FOR(j, 0, k - 1) f[idx][j] = ts[idx][mx].get(ts[j][mx]);
FOR(j, 0, k - 1) {
node &nd = ts[j][mx];
nd.t = 1;
nd.p.j = nd.s_nd.j = j;
}
FOR(idx, mx + 1, gmx) {
FOR(j, 0, k - 1) {
int tx = j;
node &nd = ts[j][idx];
nd = ts[tx][idx - 1]; // O(1) 浅拷贝
FOR(step, 1, k - 1) {
adx(tx, 1);
nd += ts[tx][idx - 1];
}
}
}
int id = -1;
REP(idx, tpr, 1) if (tl[idx] != tr[idx]) { id = idx; break; }
if (id == -1) exit(0);
tp = 0;
gs[tpr + 1] = 0;
REP(idx, tpr, 1) adx(gs[idx] = gs[idx + 1], tl[idx]);
stk[++tp] = ts[gs[1]][0];
FOR(idx, 1, id - 1) {
FOR(j, tl[idx] + 1, k - 1) {
int tx; adx(tx = j, gs[idx + 1]);
stk[++tp] = ts[tx][idx - 1];
}
}
FOR(j, tl[id] + 1, tr[id] - 1) {
int tx; adx(tx = j, gs[id + 1]);
stk[++tp] = ts[tx][id - 1];
}
REP(idx, tpr, 1) adx(gs[idx] = gs[idx + 1], tr[idx]);
REP(idx, id - 1, 1) {
FOR(j, 0, tr[idx] - 1) {
int tx; adx(tx = j, gs[idx + 1]);
stk[++tp] = ts[tx][idx - 1];
}
}
stk[++tp] = ts[gs[1]][0];
FOR(idx, 1, tp) stk[idx].t = 0;
FOR(idx, 2, tp) stk[1] += stk[idx];
output(stk[1].su), pc(10);
}
return 0;
}
E2. String (Hard Version)
\(2 \le k \le 62,1 \le q \le 10^4,1 \le |T| \le 2 \times 10^6,1 \le l \le r \le 10^{17}\)。
1
注意到 \(ts_{j,i}\) 的基本储存单位是 \(0,1,\cdots,k-1\) 整体平移 \(j\) 后组成的结果,所以不妨用 \(t_j\) 来表示这么一块。
现在考虑匹配。注意到同一块内的差分数组均为 \(1\)。不妨维护 \(T\) 的差分数组,然后找到其中非 \(1\) 的位置。这些位置的间距理应是 \(k\) 的倍数,然后将 \(T\) 缩成 \(t\)。这样整体复杂度可以除一个 \(k\),为 \(O((k+\log_{k}{V})\sum |T|)\)。
那如果没有非 \(1\) 位置呢?感性理解这样的串如果太长,必然找不到匹配;短的时候暴力跑原本的算法即可。
不存在代码实现。
1113 div2
A. You Delete, I Delete
给定长度为 \(n\) 的 \(01\) 的字符串。小 \(A\) 要删去恰好一个 \(0\),然后小 \(B\) 再删去恰好一个 \(1\)。小 \(A\) 要最大化字典序,小 \(B\) 要最小化字典序。
输出二人操作后的字符串。
\(1 \le \sum n \le 100\)。
1
注意到小 \(A\) 会删去第一个 \(0\),小 \(B\) 会删去第一个 \(1\)。
并且二人的策略互不影响。
时间复杂度 \(O(n)\)。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <cstring>
#include <random>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ld long double
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x*=y;
}
const int N = 2e6+10;
static char s[N];
inline void solve(){
scanf("%s ",s+1);
int n = std::strlen(s+1);
bool p0,p1;p0 = p1 = 0;
FOR(i,1,n){
if(s[i]=='1'&&!p1){p1 = 1;continue;}
if(s[i]=='0'&&!p0){p0 = 1;continue;}
putchar(s[i]);
}
putchar(10);
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
B. Merge to Match
给定整数集合 \(S\) 和 \(T\)。每次操作可以删除 \(S\) 中的两个数 \(x \le y\),然后加入一个数 \(z \in [x,y]\)。求能否通过若干次操作使得 \(S\) 等于 \(T\)。
\(1 \le \sum n,m \le 2 \times 10^5\)。
1
转化一下问题:将 \(T\) 中的数 \(x\) 和 \(S\) 中的数匹配。\(x\) 可以匹配一个等于它的数,或者匹配一个不大于它和一个不小于它的数。问能否将 \(T\) 中的数匹配完。如果 \(S\) 中剩余的数可以通过操作被消去。
优先进行匹配 \(1\)。然后 \(a\) 数组和 \(b\) 数组没有相同元素。\(b\) 数组每个元素给出的限制形如 \([1,i]\) 和 \([i+1,n]\) 之间要有一对匹配。
定义 \(w_i\) 表示 \([1,i]\) 的限制次数。定义 \(s_i = \sum_{j \le i}w_j\)。
不妨从前往后考虑,维护 \(ct0,ct1\) 分别表示前缀 \([1,i-1]\) 中有 \(ct0\) 个数可以使用,有 \(ct1\) 个数等待匹配。
现在我们要加入一个数。我们既可以用其匹配 \(ct1\),也可以将其加入 \(ct0\)。
考虑贪心,我们希望在后缀有足够多的数能使用的前提下,尽可能匹配 \(ct1\)。而最优情况下后缀 \(j\) 对 \(ct0\) 的限制形如 \(ct0 \ge s_j-s_{i-1}-j-i+1)\)。拆拆式子,维护 \(mi_i = s_j-j\) 的后缀最小值。如果 \(ct0\) 恰好达到限制,则必须将当前数加入 \(ct0\),否则用来匹配即可。
时间复杂度 \(O(n \log n)\)。
2
其实有一个比较牛的观察:
注意到将 \(b_i\) 和 \(a_i\) 和 \(a_{n-m+i}\) 匹配是最优的。
因为这在保证前缀有匹配的前提下,最小化了 \(a_i\);在保证后缀有匹配的前提下,最大化了 \(a_{n-m+i}\)。
代码给出的是第一种实现。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <cstring>
#include <map>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ld long double
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x*=y;
}
const int N = 2e5+10;
static int n,m,a[N],b[N];
static int ta[N],atp,tb[N],btp;
static int xx[N<<1],tp;
static int ct[N<<1],gct[N<<1];
static int s[N],w[N],mx[N];
inline void ckmax(int &x,int y){
x<y&&(x=y);
}
inline void solve(){
readx(n),readx(m);
tp = 0;
FOR(i,1,n)readx(a[i]),xx[++tp] = a[i];
FOR(i,1,m)readx(b[i]),xx[++tp] = b[i];
if(n<m)return puts("NO"),void();
std::sort(xx+1,xx+1+tp);tp = std::unique(xx+1,xx+1+tp)-xx-1;
FOR(i,1,n)a[i] = std::lower_bound(xx+1,xx+1+tp,a[i])-xx;
FOR(i,1,m)b[i] = std::lower_bound(xx+1,xx+1+tp,b[i])-xx;
FOR(i,1,n)++gct[a[i]];
FOR(i,1,tp)ct[i] = gct[i];
btp = 0;
FOR(i,1,m)
if(ct[b[i]])--ct[b[i]];
else tb[++btp] = b[i];
FOR(i,1,btp)b[i] = tb[i];
m = btp;
atp = 0;
FOR(i,1,n)
if(gct[a[i]]!=ct[a[i]])++ct[a[i]];
else ta[++atp] = a[i];
FOR(i,1,atp)a[i] = ta[i];
n = atp;
std::sort(a+1,a+1+n);
std::sort(b+1,b+1+m);
a[n+1] = 1e9;
FOR(i,0,n+1)w[i] = 0;
FOR(i,1,m){
int p = std::upper_bound(a+1,a+n+1+1,b[i])-a-1;
++w[p];
}
//tiaoshi
// FOR(i,1,n)printf("%d ",xx[a[i]]);putchar(10);
// FOR(i,1,m)printf("%d ",xx[b[i]]);putchar(10);
FOR(i,1,n)(s[i]=s[i-1])+=w[i];
mx[n+1] = -1e9;
REP(i,n,1)ckmax(mx[i]=mx[i+1],s[i]-i);
// FOR(i,1,n)printf("%d ",s[i]);putchar(10);
bool flg = 1;
int ct0,ct1;ct0 = ct1 = 0;
FOR(i,1,n){
if(ct0<=mx[i]-(s[i-1]-(i-1))){
++ct0;
}
else{
if(ct1)--ct1;
else ++ct0;
}
if(ct0<w[i]){flg = 0;break;}
ct0-=w[i],ct1+=w[i];
}
flg&=ct1==0&&!w[0];
puts(flg?"YES":"NO");
FOR(i,1,tp)gct[i] = ct[i] = 0;
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
C. Maximize the Score
给定长度为 \(2n\) 的序列 \(a\)。其中 \(1\) 到 \(n\) 均恰好出现两次。每次操作可以删去选择一个数 \(x\),即其最小下标和最大下标分别为 \(l,r\),然后删去区间 \([l,r]\) 内的所有数,贡献是 \((r-l+1)^2\)。
你可以进行任意次操作,最大化每次操作贡献和。
\(1 \le \sum n \le 2 \times 10^5\)。
1
注意到贡献区间包含/不交。可以做区间 \(dp\)。时间复杂度 \(O(n^2)\)。
注意到区间包含是不优的,因为 \((a+b)^2 \ge a^2+b^2\)。所以可以从前往后做一个线性 dp。
时间复杂度 \(O(n)\)。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <cstring>
#include <map>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ll long long
#define ld long double
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x*=y;
}
const int N = 2e5+10;
static int n,m,c[N],a[N<<1];
inline void ckmax(ll &x,ll y){
x<y&&(x=y);
}
inline ll fs(ll x){
return x*x;
}
static ll f[N];
inline void solve(){
readx(n);
FOR(i,1,2*n)readx(a[i]);
FOR(i,1,n)c[i] = 0;
FOR(i,1,2*n){
f[i] = f[i-1]+1;
if(c[a[i]])ckmax(f[i],f[c[a[i]]-1]+fs(i-c[a[i]]+1));
c[a[i]] = i;
}
output(f[2*n]),putchar(10);
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
D. Good Pair Queries
给定两个长度均为 \(n\) 的 \(01\) 字符串 \(S,T\)。定义一对字符串是好的,当且仅当 \((s,t)\) 能通过若干次操作删空。每次操作删去 \(s,t\) 在位置上重合且众数相同的子序列。
每次询问 \((s[l,r],t[l,r])\) 是否是好的。
\(1 \le \sum n,\sum q \le 2 \times 10^5\)。
1
注意到我们只关心 \((0,0)\),\((1,1)\),\((1,0)\),\((0,1)\) 这四种二元组的出现次数。
前两种是纯色的,后两种是杂色的。纯色的可以自己操作删去,杂色的不行。
发现相同数量的 \((1,0)\) 和 \((0,1)\) 可以相互抵消。否则相同数量的纯色也可以和剩余的杂色抵消。如果杂色还有剩余,则无法删空。
查询就维护一下四种前缀和。
时间复杂度 \(O(n)\)。
2
如果对好的 \((l,r)\) 计数怎么做?
对纯色数做前缀和 \(s0\),对 \((1,0)\) 赋权值 \(1\),\((0,1)\) 赋权值 \(-1\),做前缀和 \(s1\)。
则 \([l+1,r]\) 合法,等价于 \(s0_r-s0_l \ge |s1_r-s1_l|\)。把绝对值拆拆,相当于一个三维数点,可以上 cdq 分治。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <cstring>
#include <map>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ll long long
#define ld long double
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x*=y;
}
const int N = 2e5+10;
static int s[3][N],n,q;
static char a[N],b[N];
inline void solve(){
readx(n),readx(q);
scanf("%s ",a+1);
scanf("%s ",b+1);
FOR(i,1,n){
FOR(t,0,2)s[t][i] = s[t][i-1];
if(a[i]==b[i])++s[0][i];
else if(a[i]=='1')++s[1][i];
else ++s[2][i];
}
int l,r;
FOR(i,1,q){
readx(l),readx(r);
int p0 = s[0][r]-s[0][l-1];
int p1 = s[1][r]-s[1][l-1];
int p2 = s[2][r]-s[2][l-1];
int p3 = std::max(p1,p2)-std::min(p1,p2);
puts(p0>=p3?"YES":"NO");
}
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
E. Excuse for Breaks
给定长度为 \(m\) 的数组 \(p,r\) 和常数 \(n,d\)。
对一个 \(01\) 字符串,定义位置 \(p_i\) 表示以 \(i\) 结尾的 \(1\) 最长连续段长度对 \(n\) 取模的结果(整除 \(n\) 的话,则等于 \(n\))。定义 \(g_i = \sum_{j=1}^{m}[p_j = i]r_j\)。定义一个二进制字符串 \(a\) 的权值 \(f(a)\) 为:\(1\) 的数量乘上 \(d\),加上 \(\sum_{i=1}^{|a|}g_{p_i}\)。
定义 \(I(a)\) 表示与 \(a\) 等长且全是 \(1\) 的 \(01\) 字符串。求是否存在一个 \(a\),满足 \(f(a) \gt f(I(a))\)。
\(1 \le d,n \le 10^9,1 \le \sum m \le 2 \times 10^3\)。
1
朴素想法。定义 \(f_i\) 表示长度为 \(i\) 的二进制串。转移就枚举最后一个 \(1\) 连续段。
但是问题是 \(n\) 很大。且你不知道究竟要到多长。
考虑发掘一些性质:由于 \(0\) 不产生贡献,所以我们希望 \(0\) 一定单独出现。且每个 \(1\) 连续段的长度一定恰好等于一个 \(p_i\),否则删去一些 \(1\) 不劣。
在考虑一些局部情况:对于前缀形如 \([x],0,[y]\) 表示长度为 \(x\) 的 \(1\) 接上一个 \(0\) 再街上一个长度为 \(y\) 的 \(1\)。如果其权值大于等于 \(f(I(a))\),则你将其全换成 \(1\) 肯定不劣。
可以得出结论,如果不存在一个 \([x],0,[y]\) 合法,则不存在一个 \(a\) 合法。问题转化成判定是否存在合法 \([x],0,[y]\)。
考虑其与 \(f(I(a))\) 的差值。发现 \([x]\) 部分是一致的,差距在一个 \(0\) 和一个长度为 \(y\) 的连续段和平移了 \(x\) 单位后的长度为 \(y+1\) 的连续段。
\(y\) 一定恰好等于某个 \(p\),可以吗枚举;由于 \(x\) 可以任意调整,我们只需找到长度为 \(y+1\) 的区间覆盖权值和的最小值。注意到起点必然是某个 \(p_i+1\),然后双指针。
注意到 \(y = p_i+n\) 的情况具有周期性,所以不用考虑。
时间复杂度 \(O(m^2)\)。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ll __int128
#define ld long double
#define ve std::vector
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x*=y;
}
const int N = 2e3+10;
static int n,m,d;
static int v[N],p[N];
static ll sv[N];
inline void ckmin(ll &x,ll y){
x>y&&(x=y);
}
inline ll cal(int len){
int j = 0;
ll ans = 1e18+10;
bool flg = 0;
for(int i = 1;i <= m;++i){
if(!flg){
for(;j!=m&&p[j+1]-p[i]<=len;++j);
if(j==m)flg = 1,j = 0;
}
if(flg){
for(;j!=m&&p[j+1]+n-p[i]<=len;++j);
}
if(!flg)ckmin(ans,sv[j]-sv[i]);
else ckmin(ans,sv[m]-sv[i]+sv[j]);
}
// printf("len:%d ans:%d\n",len,ans);
return ans;
}
inline void solve(){
readx(n),readx(m),readx(d);
FOR(i,1,m)readx(p[i]),readx(v[i]);
for(;m&&p[m]>n;--m);
FOR(i,1,m)(sv[i]=sv[i-1])+=v[i];
FOR(i,1,m)if(sv[i]>d+cal(p[i]+1))return puts("YES"),void();
puts("NO");
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
F. Matrix Elimination
给定 \(n \times m\) 的整数矩形。定义位置 \((x,y)\) 合法,当前仅当 \(v_{x,y} \gt \sum_{j=1}^{m}[j \neq y]v_{x,j}+\sum_{i=1}^{n}[i \neq x]v_{i,y}\)。每次操作可以选择一个子矩形,将所有元素减 \(1\)。求最少操作数,能使得至少存在 \(k\) 个合法位置。无解输出 \(-1\)。
\(1 \le \sum nm \le 10^5\)。
1
注意到大多数情况下操作整个矩形是最优的。除了 \(n=1\) 或 \(m=1\),即一个序列的情况;此时只有对于位置 \(1\) 和位置 \(n\) 可能不优。特判一下。
二分即可。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ll __int128
#define ld long double
#define ve std::vector
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x*=y;
}
const int N = 1e5+10;
static int n,m,k;
static ve<ll> a[N],g[N],b[N];
static ll p[N],h[N],f[N];
static int tp;
inline bool check(ll mid,bool flg){
if(flg){
FOR(i,1,n)FOR(j,1,m)b[i][j] = a[i][j]-mid;
FOR(i,1,n)FOR(j,1,m)g[i][j] = 0;
FOR(i,1,n){
ll pw = 0;
FOR(j,1,m)pw+=b[i][j];
FOR(j,1,m)g[i][j]+=pw-b[i][j];
}
FOR(j,1,m){
ll pw = 0;
FOR(i,1,n)pw+=b[i][j];
FOR(i,1,n)g[i][j]+=pw-b[i][j];
}
int ct = 0;
FOR(i,1,n)FOR(j,1,m)ct+=b[i][j]>=g[i][j];
return ct>=k;
}
else{
FOR(i,1,tp)h[i] = p[i]-mid;
ll pw = 0;
FOR(i,1,tp)pw+=h[i];
FOR(i,1,tp)f[i] = pw-h[i];
int ct = 0;
FOR(i,1,tp)ct+=h[i]>=f[i];
if(ct>=k)return 1;
if(ct<=k-2)return 0;
if(h[1]<f[1]){
ll del = f[1]-h[1];
bool flg = del<=mid;
FOR(i,2,tp)if(h[i]>=f[i])flg&=h[i]>=f[i]+del;
if(flg)return 1;
}
if(h[tp]<f[tp]){
ll del = f[tp]-h[tp];
bool flg = del<=mid;
FOR(i,1,tp-1)if(h[i]>=f[i])flg&=h[i]>=f[i]+del;
if(flg)return 1;
}
return 0;
}
}
inline void solve(){
readx(n),readx(m),readx(k);
FOR(i,1,n)a[i].resize(m+3),b[i].resize(m+3),g[i].resize(m+10);
FOR(i,1,n)FOR(j,1,m)readx(a[i][j]);
if(k>n*m)return puts("-1"),void();
bool flg = n!=1&&m!=1;
if(!flg){
tp = 0;
FOR(i,1,n)FOR(j,1,m)p[++tp] = a[i][j];
}
ll dl = 0,dr = n*m*1e9,mid,ans = -1;
while(dl<=dr){
mid = (dl+dr)>>1;
if(check(mid,flg))ans = mid,dr = mid-1;
else dl = mid+1;
}
output(ans),putchar(10);
// printf("tmp:%d\n",(int)check(2,1));
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
G. No Balance Left
给定 \(n\) 个商品价格,和 \(m\) 个促销活动 \((a_i,b_i)\) 表示花费的钱大于等于 \(a_i\) 元可以获得 \(b_i\) 元返利。
定义你初始有 \(x\) 元合法,当且仅当你可以通过若干次操作花光所有钱。每次操作你可以选择若干商品购买(可重;花费小于等于获得返利前的 \(x\)),然后选择所有可促销活动中 \(b_i\) 最大的获得。
你需要对 \(\forall x \in [1,s]\) 输出是否合法。
不保证 \(b_i \le a_i\)。
\(1 \le n,m,v_i,s,a_i,b_i \le 1.25 \times 10^5,6s,256 MB\)。
1
注意到 \(6s\)。
你可以 bitset 处理出是否存在一组商品购买方案,使得恰好花费 \(x\) 元。
假设不存在 \(b_i \gt a_i\)。
考虑到你需要对多个初始态求出是否合法,而合法的最终态是唯一的。不妨考虑逆向一下,从最终态推导有哪些初始态是合法的。这个也可以 bitset 优化。
那存在 \(b_i \gt a_i\) 呢,记这样的 \(a_i\) 最小值为 \(mi\)。注意到对于 \(x \ge mi\),你可以先将其变到一个充分大的数 \(M\)。然后根据经典结论:对两个互质的数 \((a,b)\),其不能表示出的最大数为 \(ab-a-b\)。所以记 \(d = \gcd(I,D,c_i,b_m)\)(\(I,D\) 表示上升/下降集合,\(c_i,b_m\) 是用来选择的商品充分大时的贡献)。我们猜测 \(d \mid s\) 的所有 \(s\) 我们都能表示出来。但是显然还有一个必要条件就是存在 \(b_i \lt a_i\),否则全不合法。
时间复杂度 \(O(\dfrac{V^2}{w})\)。
卡常可以用倍增优化完全背包。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <bitset>
#include <cstring>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ull unsigned long long
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 1.25e5+10;
static std::bitset<N> A,B,C,D,E;
static int n,m,s;
static int c[N],a[N],b[N];
static bool vis[N],gvis[N];
inline void ckmax(int &x,int y){
x<y&&(x=y);
}
inline void ckmin(int &x,int y){
x>y&&(x=y);
}
signed main(){
readx(n),readx(m),readx(s);
int mx = 0;
FOR(i,1,n)readx(c[i]),ckmax(mx,c[i]);
FOR(i,1,m)readx(a[i]),readx(b[i]),ckmax(mx,a[i]);
int pres = s;
ckmax(s,mx);
A[0] = 1;
FOR(i,1,n){
int tx = c[i];
if(vis[tx])continue;
for(int j = tx;j <= s;j+=tx)vis[j] = 1;
while(1){
A|=((B=C=A)<<=tx);
if(A==C)break;
// tx<<=1;
}
}
C = D;
FOR(i,1,s)vis[i] = A[i],D[i] = 1;
// FOR(i,0,s)printf("%d ",(int)vis[i]);putchar(10);
C[0] = 1;
int j = 0,tg = 0;
int mis = s+1;
FOR(i,1,s){
if(vis[i]){
for(;j<m&&a[j+1]<=i;++j);
tg = std::__gcd(tg,i-b[j]);
if(b[j]<i){
int tx = i-b[j];
if(!gvis[tx]){
// printf("i:%d tx:%d\n",i,tx);
for(int k = tx;k <= s;k+=tx)gvis[k] = 1;
((B=C)<<=tx)&=D;
while(1){
A = B;
B|=((E=B)<<=tx);
if(B==A)break;
tx<<=1;
}
C|=B;
}
}
else if(b[j]>i)ckmin(mis,i);
}
D[i] = 0;
}
FOR(i,1,n)tg = std::__gcd(tg,c[i]);
tg = std::__gcd(tg,b[m]);
tg = std::abs(tg);
bool tflg = 0;
FOR(i,1,s)tflg|=C[i];
// printf("mis:%d tg:%d tflg:%d\n",mis,tg,tflg);
FOR(i,1,pres){
bool flg = i<mis?C[i]:tflg&&!(i%tg);
puts(flg?"YES":"NO");
}
return 0;
}

浙公网安备 33010602011771号