题目能自己找吧。
CF1602A Two Subsequences
注意到,令 \(s,t\) 为字符串,其中 \(t\) 非空串,那么有 \(s < st\)。因此答案的字符串 \(a\) 一定是单个字符 \(c\) 构成,且 \(c\) 是字符串 \(s\) 的最小字符。输出 \(c\) 和去掉一个 \(c\) 之后的 \(s'\) 即可。
#include<bits/stdc++.h>
using namespace std;
char s[105];
int n;
void Solve()
{
scanf("%s",s+1);
n=strlen(s+1);
char minn='z';
for(int i=1;i<=n;++i) minn=min(minn,s[i]);
putchar(minn);
putchar(' ');
for(int i=1;i<=n;++i)
{
if(minn==s[i]) minn=0;
else putchar(s[i]);
}
puts("");
}
int main(){
int T;
scanf("%d",&T);
while(T-->0) Solve();
return 0;
}
CF1602B Divine Array
数据范围很小。猜测在 \(O(n)\) 次变化之后整个序列就不会变化了。有一个更紧的上界是 \(O(\log n)\),证明如下:
如果两种数出现次数相同,在一次变化之后这两种数会变成一种数,称这两种数可以合并。
一个数组如果不存在可合并的数就不会改变了。并且每次合并后这个新数的出现次数至少翻倍,那么最多在 \(O(\log n)\) 次操作后就不会改变。
但是 who cares,,,能过就行。
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
typedef long long LL;
typedef pair<int,int> P;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
int a[2005],mat[2005][2005],n,t[2005];
void Solve()
{
n=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=n;++i) mat[0][i]=a[i];
for(int i=1;i<=n;++i)
{
memset(t,0,sizeof t);
for(int j=1;j<=n;++j) ++t[mat[i-1][j]];
for(int j=1;j<=n;++j) mat[i][j]=t[mat[i-1][j]];
}
int T=read();
while(T-->0)
{
int x=read(),k=read();
write(mat[min(k,n)][x]);
puts("");
}
}
int main(){
int T=read();
while(T-->0) Solve();
return 0;
}
CF1601A Array Elimination
注意到位运算上,每一位独立。把每一位拆下来,以第 \(i\) 位为例,每次操作可以消掉 \(k\) 个或 \(0\) 个 \(i\) 位上的 \(1\)。因此答案是所有数每位上 \(1\) 出现次数的 \(\gcd\)。特殊的,如果序列全是 \(0\),\(k\) 可以取 \([1,n]\) 里的任意整数。
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
typedef long long LL;
typedef pair<int,int> P;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int n,a[200005];
void Solve()
{
n=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=n;++i) if(a[i]) goto aborted;
for(int i=1;i<=n;++i) write(i),putchar(i==n?'\n':' ');
return ;
aborted:;
int st=0;
for(int i=0;i<30;++i)
{
int x=0;
for(int j=1;j<=n;++j) x+=(a[j]>>i)&1;
st=gcd(x,st);
}
for(int i=1;i<=st;++i) if(st%i==0) write(i),putchar(' ');
puts("");
}
int main(){
int T=read();
while(T-->0) Solve();
return 0;
}
gap1
CF1601B Frog Traveler
人生苦短,,,我写线段树优化建图。
对每一个点开一个虚点表示滑下去之前的点就行了。
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
typedef long long LL;
typedef pair<int,int> P;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
struct Edge{
int t,v;
Edge(int T=0,int V=0){t=T,v=V;}
bool operator < (Edge ano) const {return v>ano.v;}
};
vector<Edge> G[2000005];
int n,a[300005],b[300005],cnt,id[1200005];
#define Mm int mid=(l+r)>>1
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
void build(int l,int r,int now)
{
id[now]=++cnt;
if(l==r)
{
G[id[now]].push_back(Edge(l,0));
return ;
}
Mm;
build(l,mid,lc(now)),build(mid+1,r,rc(now));
G[id[now]].push_back(Edge(id[lc(now)],0));
G[id[now]].push_back(Edge(id[rc(now)],0));
}
void modify(int l,int r,int now,int x,int y,int p)
{
if(x<=l && r<=y)
{
G[p].push_back(Edge(id[now],1));
return ;
}
Mm;
if(x<=mid) modify(l,mid,lc(now),x,y,p);
if(mid<y) modify(mid+1,r,rc(now),x,y,p);
}
#undef Mm
#undef lc
#undef rc
int dis[2000005],pre[2000005];
void Dijkstra()
{
memset(dis,63,sizeof dis);
dis[n]=0;
priority_queue<Edge> Q;
Q.push(Edge(n,0));
while(!Q.empty())
{
Edge pt=Q.top();
Q.pop();
if(pt.v>dis[pt.t]) continue;
int u=pt.t;
for(auto st:G[u])
{
int v=st.t,w=st.v;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
Q.push(Edge(v,dis[v]));
pre[v]=u;
}
}
}
}
int main(){
n=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=n;++i) b[i]=read();
for(int i=1;i<=n;++i) G[i].push_back(Edge(i+b[i]+n,0));
cnt=2*n;
build(1,n,1);
for(int i=1;i<=n;++i) modify(1,n,1,max(1,i-a[i]),i,i+n);
++cnt;
for(int i=1;i<=n;++i) if(a[i]==i) G[i+n].push_back(Edge(cnt,1));
Dijkstra();
int ans=1e9;
for(int i=1;i<=n;++i) if(a[i]==i) ans=min(ans,dis[i+n]+1);
if(ans>=1e8)
{
puts("-1");
return 0;
}
write(ans),puts("");
stack<int> S;
int now=cnt;
while(now!=2*n)
{
if(now<=n) S.push(now);
now=pre[now];
}
while(!S.empty()) write(S.top()),putchar(' '),S.pop();
puts("0");
return 0;
}
gap2
CF1601C Optimal Insertion
显然 \(b\) 从小到大顺序插入最优。证明如下。
假设在两个位置先后插入了 \(x,y\),其中 \(x < y\)。交换 \(x,y\) 之后,多出了 \((y,x)\) 这一个逆序对,并且答案不会变小。因此顺序插入最优。
当然你可以看样例看出来。
然后考虑构造出在 \(a\) 中插入 \(b\) 后新形成的序列 \(c\),直接计算其逆序对个数。注意到我们已知 \(b\) 按顺序插入,没必要管插入的具体顺序,只需要知道是放在 \(a\) 的哪个元素之前就行了,并且定义 \(p_i\) 表示 \(b_i\) 放在 \(a_{p_i}\) 之前。
注意到我们还可以放在 \(a\) 的最后,新加入一个极大值即可。
然后单调性分治,我们定义 Solve(L,R,l,r)
表示 \(b_{l,\cdots ,r}\) 要插入到 \(a_{L,\cdots ,R}\) 之前。令 \(mid = \left\lfloor \dfrac{l+r}{2} \right\rfloor\)我们确定了 \(b_{mid}\) 的位置之后,也确定剩下两块 \(b\) 插入的位置的范围。找到 \(b_{mid}\) 插入的最优位置可以直接 \(O(n)\),时间复杂度显然是 \(O(n \log m)\) 的。
然后就做完了呗……实现可以看代码。一定要清空干净。
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
typedef long long LL;
typedef pair<LL,LL> P;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
LL read()
{
LL x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(LL x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
LL lowbit(LL x){return x&(-x);}
LL n,m,a[1000005],b[1000005],B[2000005],len,p[1000005],c[2000005];
struct binaryIndexedTree{
LL tr[2000005];
void clear(LL up){for(LL i=0;i<=up;++i) tr[i]=0;}
void modify(LL x,LL val){for(LL i=x;i<=len;i+=lowbit(i)) tr[i]+=val;}
LL query(LL x){LL ans=0;for(LL i=x;i;i^=lowbit(i)) ans+=tr[i];return ans;}
}bit;
void Divide(LL L,LL R,LL l,LL r)
{
if(l>r) return ;
LL mid=(l+r)>>1,val=b[mid],inv=0;
for(LL i=L;i<=R;++i) if(val>a[i]) ++inv;
LL ret=inv,pos=L;
for(LL i=L+1;i<=R;++i)
{
if(val<a[i-1]) ++inv;
else if(val>a[i-1]) --inv;
if(inv<ret) inv=ret,pos=i;
}
p[mid]=pos;
Divide(L,pos,l,mid-1),Divide(pos,R,mid+1,r);
}
void Solve()
{
n=read(),m=read();
for(LL i=1;i<=n;++i) a[i]=read();
for(LL i=1;i<=m;++i) b[i]=read();
for(LL i=1;i<=n;++i) B[i]=a[i];
for(LL i=1;i<=m;++i) B[i+n]=b[i];
sort(b+1,b+1+m);
sort(B+1,B+1+n+m);
len=unique(B+1,B+1+n+m)-B-1;
for(LL i=1;i<=n;++i) a[i]=lower_bound(B+1,B+1+len,a[i])-B;
for(LL i=1;i<=m;++i) b[i]=lower_bound(B+1,B+1+len,b[i])-B;
a[++n]=++len;
Divide(1,n,1,m);
LL cnt=0,pos=1;
for(LL i=1;i<=n;++i)
{
while(pos<=m && p[pos]==i) c[++cnt]=b[pos++];
c[++cnt]=a[i];
}
bit.clear(len);
LL ans=0;
for(LL i=1;i<=n+m;++i)
{
ans+=bit.query(len)-bit.query(c[i]);
bit.modify(c[i],1);
}
write(ans),puts("");
}
int main(){
LL T=read();
while(T-->0) Solve();
return 0;
}
有一个疑点是,\(b_mid\) 插入的最优位置可以有很多个,为什么没有影响?
CF1601D Difficult Mountain
人类智慧题……瞎猜了几个贪心交上去竟然过了。
本来这种题的套路是按某种方式排序之后 dp(可能需要数据结构优化),但是这个题可以直接排了过。
将人按 \(\max(a_i,s_i)\) 为第一关键字,\(s_i\) 为第二关键字,排序,然后顺序判断一个人能否爬山,能爬就爬。可以证明答案最优。
但证明是真不会……还不如去写了 \(O(n \log n)\) 的做法……找个时间补一补。
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
typedef long long LL;
typedef pair<int,int> P;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
struct node{
int s,a;
void scan(){s=read(),a=read();}
bool operator < (node ano) const {return max(s,a)<max(ano.s,ano.a) || (max(s,a)==max(ano.s,ano.a) && s<ano.s);}
}clm[500005];
int n,d;
int main(){
n=read(),d=read();
for(int i=1;i<=n;++i) clm[i].scan();
sort(clm+1,clm+1+n);
int ans=0;
for(int i=1;i<=n;++i)
{
if(d<=clm[i].s)
{
++ans;
d=max(d,clm[i].a);
}
}
write(ans);
return 0;
}
CF1601E Phys Ed Online
询问相当于从 \(l\) 开始,\(l,l+k,l+2k, \cdots\) 取一次 \([l,l]\) 中的最小值,\([l,l+k]\) 中的最小值,\([l,l+2k]\) 中的最小值……然后累加。
注意到这些位置在模 \(k\) 的意义下同余,并且每次新增加的取值个数有 \(k+1\) 个(我们将上一个端点也包含),我们定义 \(b_i\) 为 \([i-k,i]\) 中的最小值。
然后考虑倍增,定义 \(nxt_{i,j}\) 表示从 \(i\) 开始,第 \(2^j\) 个 \(b\) 值小于 \(b_i\) 的位置。求 \(nxt_{i,0}\) 可以单调栈。有了 \(nxt\) 求值也不会很难,定义 \(cst_{i,j}\) 为从 \(i-k\)(这里要非常注意,,)跳至 \(j\) 需要的最少花费。
算了简单题可以看代码。
#include<bits/stdc++.h>
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<18,stdin),p1==p2)?EOF:*p1++)
typedef long long LL;
typedef pair<LL,LL> P;
#define mp make_pair
LL read()
{
LL x=0,f=1;
char c=getchar();
while(c<'0' || c>'9') f=(c=='-'?-1:f),c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x*f;
}
void write(LL x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
LL n,q,K,a[300005],st[22][300005],lgs[300005],blk[300005],nxt[22][300005],cst[22][300005];
LL query(LL l,LL r)
{
LL k=lgs[r-l+1];
return min(st[k][l],st[k][r-(1<<k)+1]);
}
int main(){
n=read(),q=read(),K=read();
for(LL i=2;i<=n;++i) lgs[i]=lgs[i>>1]+1;
for(LL i=1;i<=n;++i) st[0][i]=a[i]=read();
for(LL i=1;i<=20;++i) for(LL j=1;j+(1<<i)-1<=n;++j) st[i][j]=min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
for(LL i=1;i<=n;++i) blk[i]=query(max(i-K,1ll),i);
for(LL i=1;i<=K;++i)
{
stack<LL> S;
for(LL j=i;j<=n;j+=K)
{
while(!S.empty() && blk[j]<blk[S.top()]) nxt[0][S.top()]=j,S.pop();
S.push(j);
}
}
for(LL i=1;i<=20;++i) for(LL j=1;j+(1<<i)-1<=n;++j) nxt[i][j]=nxt[i-1][nxt[i-1][j]];
for(LL i=1;i<=n;++i) cst[0][i]=(nxt[0][i]-i)/K*blk[i];
for(LL i=1;i<=20;++i) for(LL j=1;j+(1<<i)-1<=n;++j) cst[i][j]=cst[i-1][j]+cst[i-1][nxt[i-1][j]];
while(q-->0)
{
LL l=read(),r=read(),ans=a[l],now=l+K;
if(now<=r) for(LL j=20;~j;--j) if(nxt[j][now]<=r && nxt[j][now]) ans+=cst[j][now],now=nxt[j][now];
if(now<=r) ans+=(r-now+K)/K*blk[now];
write(ans),puts("");
}
return 0;
}
gap3
CF1601F Two Sorts
定义 \(b_i\) 为 \(a\) 的逆变换(相当于 \(i\) 的排名),显然有 \(a_{b_i} = b_{a_i} = i\)。注意到 \(b_i\) 也是排列,将 \(i\) 替换成 \(b_i\) 结果不变,答案就是:
下面的东西一定要对着代码看,真的很难讲懂。
然后考虑怎么算这个东西。我们将一个数 \(x\) 掰开,拆成 \(p,q\) 两部分,然后分两部分处理,不难发现 \(p,q\) 位数相等,或者说 \(q\) 位数为数据范围位数的二分之一(即 \(\sqrt n = 10^6\))的时候最优。那假设 \(q\) 是一个可以有前导零的位数最多为六的整数,\(p\) 不能存在前导 \(0\)。我们先处理 \(q\)(可以直接 \(O(10^7)\) 做,按字典序枚举 \(q\),这个过程可以用搜索),再处理 \(p\)(类似的用搜索)。
定义两个东西 \(rk_i,rk'_i\),前者表示在 \(1 \sim n\) 中,\(i\) 的排名是多少和在 \(1 \sim 10^7-1\) 中,\(i\) 的排名是多少。注意到处理完这两个东西 \(rk_p\) 和 \(rk'_q\) 之后,我们可以求出 \(rk_{\overline{pq}} = rk_p + rk'_q\)。同时,因此我们可以将 \(i = \overline{pq}\) 的贡献 \(rk_i - i\) 拆成两部分,即 \(((rk_p - 10^6 \times p)+(rk'_q - q)) \bmod 998244353\)。
然后比较烦人的是,这个东西还要去取模,,,但是注意到 \((rk_p - 10^6 \times p)\) 固定,我们要管理的主要是 \((rk'_q - q)\),其值要么减去 \(998244353\) 要么不减。我们处理完 \(rk'_q - q\) 的值之后,将位数相同的 \(q\) 的 \(rk'_q - q\) 的值排序,然后在算贡献的时候二分,求出需要减去多少个 \(998244353\) 即可。
然后细节不多,实现很妙,一定要仔细看。很难见到的一道看代码能比看题解更清楚的题目。
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
typedef long long LL;
typedef pair<LL,LL> P;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
LL read()
{
LL x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(LL x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
const LL MOD1=998244353,MOD2=1e9+7;
vector<LL> G[7];
LL n,rnk,sum[7],ans,pw[7],siz[7];
void dfs(LL dgt,LL val)
{
++rnk;
G[dgt].push_back((rnk-val+MOD1)%MOD1);
if(dgt==6) return ;
for(LL i=0;i<=9;++i) dfs(dgt+1,val*10+i);
}
void red(LL dgt,LL val)
{
if(val>n) return ;
if(dgt>=1)
{
if(val*1000000>n/10 && (val+1)*1000000-1<=n)
{
for(LL i=0;i<=6;++i)
{
LL st=(rnk%MOD1-val*pw[i]%MOD1+MOD1)%MOD1,pos=lower_bound(G[i].begin(),G[i].end(),MOD1-st)-G[i].begin();
ans=(ans+st*siz[i]+sum[i]-MOD1*(siz[i]-pos))%MOD2;
}
for(LL i=0;i<=6;++i) rnk+=siz[i];
return ;
}
++rnk,ans=(ans+(rnk%MOD1-val%MOD1+MOD1)%MOD1)%MOD2;
}
for(LL i=!dgt;i<=9;++i) red(dgt+1,val*10+i);
}
int main(){
n=read();
dfs(0,0);
pw[0]=1;
for(LL i=0;i<=6;++i)
{
if(i) pw[i]=pw[i-1]*10;
sort(G[i].begin(),G[i].end());
for(auto st:G[i]) sum[i]=(sum[i]+st)%MOD2;
siz[i]=LL(G[i].size());
}
rnk=0;
red(0,0);
write(ans);
return 0;
}