CF Round1642
D 想到了翻转前缀的做法,可惜没有写出来,果然还是太弱了……
%%% wly 构造神仙
今天上午补完了题,E 题意外的自己想出来了
ABC 不写了吧……
D 首先可以插入一个回文串 \(\texttt{abc} \dots \texttt{cba}\) ,如果插在前缀 \(\texttt{abc}\dots\) 后,那么前面的串可以抛下不管,效果相当于翻转前缀
结果乱胡出来一个操作次数 \(3n^2\) 的构造……直觉太差了……
事实上 \(\text{flip}\) 两次即可,看看代码吧
#include <cstdio>
#include <algorithm>
#include <vector>
#define xp first
#define yp second
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
const int _=503;
typedef pair<int,int> pii;
int a[_],p[_],o[_],n,cur;
pii b[_];
vector<pii> res;
vector<int> splt;
void flip(int t){
if(t<2) return;
for(int i=1;i<=t;++i)
res.emplace_back(pii(cur+++t,b[p[i]].xp));
cur+=t;
splt.emplace_back(2*t);
reverse(p+1,p+t+1);
for(int i=1;i<=n;++i) o[p[i]]=i;
}
void solve(){
n=read();cur=0;
for(int i=1;i<=n;++i) b[i]=pii(a[i]=read(),i);
sort(b+1,b+n+1);
for(int i=1,t=1;i<=n;i=t){
while(t<=n&&b[t].xp==b[i].xp) ++t;
if((t-i)&1) {puts("-1");return;}
}
res.clear();splt.clear();
for(int i=1;i<=n;++i) p[b[i].yp]=i;
for(int i=1;i<=n;++i) o[p[i]]=i;
for(int i=1;i<=n;++i){
int e=o[i];
flip(e-1);flip(e);
}
for(int i=1;i<=n;i+=2) splt.emplace_back(2);
printf("%lu\n",res.size());
for(pii e:res) printf("%d %d\n",e.xp,e.yp);
printf("%lu\n",splt.size());
for(int e:splt) printf("%d ",e);
putchar('\n');
}
int main(){
int T=read();
while(T--) solve();
return 0;
}
比想象中的好写一些,果然实现是关键
E 首先容易用并查集处理 0 区间的覆盖
考虑 1 区间发现只有被 0 区间覆盖得只剩一个数时才有贡献,我们要求出满足这个条件的时间点
那么离线下来记录每个位置被 0 区间覆盖的时间戳最小值,那么对每个满足条件的 1 区间区间求 \(\max\)
将这个时间点与自身出现的时间点取 \(\max\),那么就可以求出 1 区间最早的贡献在哪里,扫一遍求答案即可
#include <cstdio>
#include <vector>
#include <algorithm>
#pragma GCC optimize(2,3,"Ofast")
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
int n,q;
const int _=200003;
int nx[_],dn[18][_],tg[_],lg[_];
int nxt(int i){if(nx[i]==i) return i;return nx[i]=nxt(nx[i]);}
int qry(int l,int r){
int k=lg[r-l+1];
return max(dn[k][l],dn[k][r-(1<<k)+1]);
}
struct sta{int l,r,t;};
vector<sta> vt;
vector<int> vr[_];
int vx[_];
bool res[_];
int main(){
n=read();q=read();
for(int i=1;i<=n+1;++i) nx[i]=i;
for(int i=1;i<=q;++i){
int o=read();
if(o) vx[i]=read();
else{
int l=read(),r=read(),t=read();
if(t) vt.emplace_back((sta){l,r,i});
else{
int e=l;
while(nxt(e)<=r)
e=nxt(e),tg[e]=1,dn[0][e]=i,nx[e]=e+1;
}
}
}
for(int i=2;i<=n;++i) tg[i]+=tg[i-1],lg[i]=lg[i>>1]+1;
for(int t=1;t<18;++t)
for(int i=1;i+(1<<t)-1<=n;++i)
dn[t][i]=max(dn[t-1][i],dn[t-1][i+(1<<(t-1))]);
for(sta o:vt){
if(tg[o.r]-tg[o.l-1]<o.r-o.l) continue;
int x=nxt(o.l);
vr[max(qry(o.l,o.r),o.t)].emplace_back(x);
}
for(int i=1;i<=q;++i){
for(int x:vr[i]) res[x]=1;
if(vx[i]){
if(dn[0][vx[i]]&&dn[0][vx[i]]<=i){
puts("NO");
continue;
}
if(res[vx[i]]){
puts("YES");
continue;
}
puts("N/A");
}
}
return 0;
}
F 一道妙题
假设 \(m=1\),那么很好做
我们不妨实现这样一个操作 \(f(i,S)\) 表示 \(i\) 数组与集合 \(S\) 中有没有数组不冲突
这个开个哈希表查询,动态维护集合 \(S\) 即可
此时我们就可以用双指针,对 \(w\) 排序后利用 \(f\) 对每一个 \(i\) 找到最小的满足 \(i,j\) 不冲突的 \(j\),由于 \(w_i\) 只会增大,那么 \(w_j\) 递减才有可能更优
现在数组大小可以到 5 ,考虑如何实现新的 \(f\)
我们发现之所以不能直接沿用 \(m=1\) 的做法,是因为同一对冲突的数组会被计算多次
考虑容斥 \(\sum_{i=1}^m {m\choose i}(-1)^i = -[m>0]\)
那么把一个数组所有子集 Hash
后再送进哈希表里,每个子集有一个 \((-1)^{|S|}\) 的权值,将权值求和后就可以判断 \(S\) 中到底有多少个数组和 \(i\) 冲突
#include <cstdio>
#include <unordered_map>
#include <algorithm>
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
const int _=100003;
typedef unsigned long long ull;
const ull D=1000000007;
int a[_][5],w[_],p[_];
ull b[_][32];
int n,m;
unordered_map<ull,int> c;
bool cmp(const int i,const int j){return w[i]<w[j];}
void ins(int i){for(int s=1;s<(1<<m);++s) ++c[b[p[i]][s]];}
void del(int i){for(int s=1;s<(1<<m);++s) --c[b[p[i]][s]];}
int calc(int i){
int r=0;
for(int s=1;s<(1<<m);++s)
if(__builtin_parity(s)) r+=c[b[p[i]][s]];
else r-=c[b[p[i]][s]];
return r;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;++i){
for(int j=0;j<m;++j) a[i][j]=read();
sort(a[i],a[i]+m);
for(int s=1;s<(1<<m);++s){
int pr=1;b[i][s]=0;
for(int j=0;j<m;++j)
if(s>>j&1){
b[i][s]=b[i][s]*D+(a[i][j]-pr+1);
pr=a[i][j];
}
}
w[p[i]=i]=read();
}
sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;++i) ins(i);
int mx=n,res=0x7fffffff;
for(int i=1;i<=mx;++i){
bool fl=0;
del(i);
while(calc(i)<mx-i) del(mx--),fl=1;
if(fl) ins(++mx),res=min(res,w[p[i]]+w[p[mx]]);
}
if(res==0x7fffffff) puts("-1");
else printf("%d\n",res);
return 0;
}