• Codeforces Round #751 (Div. 1 & 2) Solutions


    题目能自己找吧。

    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\) 结果不变,答案就是:

    \[\left(\sum_{i=1}^n ((b_i-i) \bmod 998244353)\right) \bmod 10^9+7 \]

    下面的东西一定要对着代码看,真的很难讲懂。

    然后考虑怎么算这个东西。我们将一个数 \(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;
    }
    
  • 相关阅读:
    (第二周)效能测试
    (第二周)新小学四则运算
    (第二周)项目点评
    (第二周)通读《构建之法》有感
    (第二周)scrum站立会议
    (第二周) 燃尽图
    (第一周)工作总结
    (第一周)小学四则运算
    软件工程第三次作业
    软件工程第二次作业
  • 原文地址:https://www.cnblogs.com/amagaisite/p/15563743.html
Copyright © 2020-2023  润新知