加载中…

返回上一页

CSP-S模拟13(CF选做)

下发文件和题解

今天的题目均来自 Codeforces,算是个欢乐赛吧. 但是还是爆零爆的很惨分数拉差很大.

A. 排序 CF1375E

首先观察 n 的大小只有 1000,那么可以先预处理出全部逆序对. 优先按照其前面较大的那个数的位置升序排序,如果要是出现一个数在这个序列中有多个逆序对就按照后面较小的那个数的大小排序,较大的在前、较小的在后.

比如下面这个序列:

7 1 3 4 5 2 6

处理出的第一个位置的数和后面的逆序对有这些(后面的数同理):

7 1

7 3

7 4

7 5

7 2

7 6

按照大小排好序之后是这样的:

7 6

7 5

7 4

7 3

7 2

7 1

对应到要交换的位置上:

1 7

1 5

1 4

1 3

1 6

1 2

可以发现 1 这个数是最后才被交换的.

因此这么做就能够保证当前的位置最后交换完一定能变成当前数.

交换完以后是这样的:

1 2 4 5 6 3 7

因此就可以证明上述做法的正确性.

对于 CodeForces 上的题,a[i] 可能相等. 这样在判断的时候加一个 if(a[i]==a[j]) return i>j; 即可.

点击查看代码
#include<bits/stdc++.h>
#define ll int
#define rg register
#define sinline static inline
#define rll rg ll
#define maxn 1001
#define pll pair<ll,ll>
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
sinline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
	return f?-x:x;
}
sinline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|'0');
}
ll n,a[maxn],id[maxn];
vector<pll> g;
sinline bool cmp(rg pll x,rg pll y) { if(x.first==y.first) return a[x.second]>a[y.second]; return x.first<y.first; }
int main()
{
	n=read();for(rll i=1;i<=n;i++) id[a[i]=read()]=i;for(rll i=n;i;i--) for(rll j=i+1;j<=n;j++) if(a[i]>a[j]) g.push_back((pll) { i,j });
	sort(g.begin(),g.end(),cmp);
	write(g.size());putn;for(rll i=0;i<g.size();i++) write(g[i].first),put_,write(g[i].second),putn;
	return 0;
}

B. Xorum CF1427E

随机化乱搞能拿 86,然而我赛时忘记输出步数了……

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define sinline static inline
#define rll rg ll
#define maxn 100000001
#define pll pair<ll,ll>
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
sinline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
	return f?-x:x;
}
sinline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|'0');
}
ll x,sz,kk,rd1,rd2,rd3,tot;
bool fl[maxn];
ll a0[100001],a1[100001],a2[100001];
vector<ll> g;
int main()
{
	srand(time(0));x=read();a0[++tot]=1;a1[tot]=(x);a2[tot]=(x);fl[x]=1;fl[x<<1]=1;g.emplace_back(x),g.emplace_back(x<<1);x=x*100;
	while(1)
	{
		sz=g.size(),rd1=rand()%sz,rd2=rand()%sz,rd3=rand()&1;while(rd2==rd1) rd2=rand()%sz;
		rd1=g[rd1];rd2=g[rd2];// cout<<rd1<<' '<<rd2<<' '<<rd3<<endl;
		if(rd3)
		{
			if(rd1+rd2>=x||fl[rd1+rd2]) continue;
			a0[++tot]=1;a1[tot]=(rd1);a2[tot]=(rd2);fl[kk=rd1+rd2]=1;g.emplace_back(kk);
		}
		else
		{
			if((rd1^rd2)>=x||fl[rd1^rd2]) continue;
			a0[++tot]=2;a1[tot]=(rd1);a2[tot]=(rd2);fl[kk=(rd1^rd2)]=1;g.emplace_back(kk);
		}
		if((kk&1)&&fl[kk-1]) { a0[++tot]=2;a1[tot]=(kk-1);a2[tot]=(kk);if(tot>100000) tot=0,memset(fl,0,sizeof(fl)),fl[x/100]=fl[(x/100)<<1]=1,g.clear(),g.emplace_back(x/100),g.emplace_back((x/100)<<1),a0[++tot]=1,a1[tot]=(x/100),a2[tot]=(x/100);else break; }
		if((!(kk&1))&&fl[kk+1]) { a0[++tot]=2;a1[tot]=(kk);a2[tot]=(kk+1);if(tot>100000) tot=0,memset(fl,0,sizeof(fl)),fl[x/100]=fl[(x/100)<<1]=1,g.clear(),g.emplace_back(x/100),g.emplace_back((x/100)<<1),a0[++tot]=1,a1[tot]=(x/100),a2[tot]=(x/100);else break; }
		// for(auto i:fl) write(i),put_;putn;
	}
	write(tot);putn;for(rll i=1;i<=tot;i++) write(a0[i]),put_,write(a1[i]),put_,write(a2[i]),putn;
	return 0;
}

既然要求最终结果一定是 1,且我现在只有一个数 x,那么一定是需要把x 二进制中前面的每一位消掉才能得到 1.

那么如何能够构造出一个这样的数呢?

很显然,消除最高位.

对于异或,显然 3 个数两两异或只能来回团团转. 要想产生新数只能进行加法操作.

那么一种算法就是把一个构造出的数 x 不断自己加自己,就可以看成是左移(加 1 次就是左移 1,加 2 次就是左移 2).

每次把数 x 不断进行左移,直至和原数的最高位对齐(如果看不懂先往下看再回来就明白了). 左移之后这个数记为 a.

b = x ^ a ,那么 b 可以将 x 的最高位消去(想一想,为什么?).

c = a + b,那么 c 的最低位一定和 b 的相同(因为 a 是经过左移的嘛). 它的最高位是 a 的最高位左移一位,且它中间的位数正好是最高位的进位. 这样就得到了最高位的那个 1.

不断进行以上操作,最终一定可以消成 1.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define sinline static inline
#define rll rg ll
#define maxn 1000001
#define lowbit(x) (x&-x)
#define pll pair<ll,ll>
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
sinline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
	return f?-x:x;
}
sinline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|'0');
}
struct node { ll op,a,b; };
ll n;
vector<node> g;
sinline void upd(rll& x)
{
	rll a=x,ta=x>>1,b,c,d,e,f;while(ta) g.push_back((node) { 1,a,a }),a<<=1,ta>>=1;
	b=a^x;g.push_back((node) { 2,a,x });
	c=a+b;g.push_back((node) { 1,b,a });
	d=a<<1;g.push_back((node) { 1,a,a });
	e=c^d;g.push_back((node) { 2,c,d });
	f=x^e;g.push_back((node) { 2,x,e });
	while(a^lowbit(a)) { if(a&f) g.push_back((node) { 2,a,f }),a^=f; g.push_back((node) { 1,f,f }),f<<=1; }
	g.push_back((node) { 2,x,a });x^=a;
}
int main()
{
	n=read();while(n^1) upd(n);
	write(g.size());putn;for(rll i=0;i<g.size();i++) write(g[i].a),put_,putchar(g[i].op&1?'+':'^'),put_,write(g[i].b),putn;// 注意比赛的题要求输出的格式不同,改一下即可.
	return 0;
}

C. 有趣的区间问题 CF1609F

这个题我用的树状数组切的,看有好多人用线段树、CDQ 分治什么的.

最大值只有 1018,归到二进制上最多只有 60 位. 那么一定是去枚举 popcount.

一个暴力的做法是去枚举 popcount,枚举区间的右端点,用单调栈维护左端点,维护这个区间内的最大值与最小值在二进制下的 popcount 是否为当前枚举的值.

那么考虑优化一下这个做法. 维护几个东西.

需要维护什么:

需要维护两个序列. 要对每个序列的区间执行覆盖 0/1,而且需要查询对应区间内两个序列里每个数的乘积.

那么自然想到了魔改 bit,差分一下.

发现对于一次操作把其中的一段区间覆盖成 1,那么如果后面修改的时候只要变一定是整段都变为 0(因为最值变了). 那么在单调栈的时候记录一个时间戳,以便后边修改.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define sinline static inline
#define rll rg ll
#define maxn 1000001
#define lowbit(x) (x&-x)
#define pll pair<ll,ll>
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
sinline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
	return f?-x:x;
}
sinline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|'0');
}

template<typename t,size_t sz>
class bit
{
#define lowbit(x) (x&-x)
private:
	t c[sz];
public:
	inline void clear() { memset(c,0,sizeof(c)); }
	inline void update(rll x,rll n,rll v) { for(rll i=x;i<=n;i+=lowbit(i)) c[i]+=v; }
	inline ll query(rll x) { rll ans=0; for(rll i=x;i;i-=lowbit(i)) ans+=c[i]; return ans; }
#undef lowbit
};

ll n,a[maxn],cnt[maxn],tmx,tmn,sum,ans;
sinline ll bitcnt(rll x) { return __builtin_popcountll(x); }
bit<ll,maxn> t[4];
stack<ll> smx,smn;
vector<pll> mx[64],mn[64];
ll lmx[maxn],lmn[maxn];
bool fl[maxn];
sinline void upd1(rll l,rll r,rll v) { t[0].update(l,n,v); t[0].update(r+1,n,-v); t[1].update(l,n,v*(l-1)); t[1].update(r+1,n,(-v)*r); }
sinline ll query1(rll l,rll r) { return r*t[0].query(r)-(l-1)*t[0].query(l-1)-t[1].query(r)+t[1].query(l-1); }
sinline void upd2(rll l,rll r,rll v) { t[2].update(l,n,v); t[2].update(r+1,n,-v); t[3].update(l,n,v*(l-1)); t[3].update(r+1,n,(-v)*r); }
sinline ll query2(rll l,rll r) { return r*t[2].query(r)-(l-1)*t[2].query(l-1)-t[3].query(r)+t[3].query(l-1); }
sinline void upd(rg bool op,rll l,rll r,rll v)
{
	if(!op) sum+=v*query2(l,r),upd1(l,r,v);
	else sum+=v*query1(l,r),upd2(l,r,v);
}
int main()
{
	smx.push(0);smn.push(0);n=read();
	for(rll i=1;i<=n;i++)
	{
		fl[cnt[i]=bitcnt(a[i]=read())]=1;
		while(smx.size()>1&&a[i]>a[smx.top()]) mx[cnt[smx.top()]].push_back((pll) { i,smx.top() }),smx.pop();lmx[i]=smx.top()+1,smx.push(i);
		while(smn.size()>1&&a[i]<a[smn.top()]) mn[cnt[smn.top()]].push_back((pll) { i,smn.top() }),smn.pop();lmn[i]=smn.top()+1,smn.push(i);
	}
	for(rll x=0;x<=62;x++) if(fl[x])
	{
		sort(mx[x].begin(),mx[x].end());sort(mn[x].begin(),mn[x].end());
		t[0].clear();t[1].clear();t[2].clear();t[3].clear();tmx=tmn=sum=0;
		for(rll i=1;i<=n;i++)
		{
			if(cnt[i]==x) upd(0,lmx[i],i,1),upd(1,lmn[i],i,1);
			while(tmx<mx[x].size()&&mx[x][tmx].first==i) upd(0,lmx[mx[x][tmx].second],mx[x][tmx].second,-1),tmx++;
			while(tmn<mn[x].size()&&mn[x][tmn].first==i) upd(1,lmn[mn[x][tmn].second],mn[x][tmn].second,-1),tmn++;
			ans+=sum;
		}
	}
	write(ans);
	return 0;
}

D. 无聊的卡牌问题

显然 kazuto dalao 比我讲得更好. Link

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define sinline static inline
#define rll rg ll
#define maxn 10001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
sinline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
	return f?-x:x;
}
sinline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|'0');
}
ll n,cnt,ls,k,vis[maxn],vis1[maxn],t[maxn],p[maxn],fl[maxn];
ll s[maxn],top;
vector<ll> g[maxn],h[maxn],ans;
deque<ll> q[2];
stack<ll> s1;
sinline void dfs(rll x)
{
	if(!g[x].size()) q[vis1[x]].push_back(x);
	for(rll i=0;i<g[x].size();i++) { rll to=g[x][i];dfs(to);t[to]=x; }
}
sinline void upd(rll x)
{
	ans.push_back(x); if(!t[x]) return; g[t[x]].pop_back();
	if(g[t[x]].empty()) q[vis1[t[x]]].push_front(t[x]);
}
int main()
{
	n=read()*6;for(rll i=1;i<=n;i++) vis[i]=1;
	for(rll i=1,x;i<=n/2;i++) vis[read()]=0;
	for(rll i=1;i<=n;i++)
	{
		s[++top]=i;
		if(top>2&&vis[s[top]]==vis[s[top-1]]&&vis[s[top]]==vis[s[top-2]])
		{
			p[s[top-2]]=++cnt;vis1[cnt]=vis[s[top]];
			h[cnt].push_back(s[top-2]);h[cnt].push_back(s[top-1]);h[cnt].push_back(s[top]);
			while((!s1.empty())&&s1.top()>s[top-2])
				g[cnt].push_back(p[s1.top()]),fl[p[s1.top()]]=1,s1.pop();
			s1.push(s[top-2]);top-=3;
		}
	}
	for(rll i=1;i<=cnt;i++)
		if(!fl[i]) dfs(i),k+=vis1[i];
	for(rll i=1;i<=n/6;i++)
	{
		upd(q[0].front()); q[0].pop_front();
		if(i==n/6) { if(ls) ans.push_back(ls);else upd(q[1].front()); }
		else
		{
			rll x=q[1].front();k-=(vis1[x]==1&&!fl[x]);
			if((!k)&&(!fl[x])&&vis1[x]==1) ls=x,q[1].pop_front();
			upd(q[1].front());q[1].pop_front();
		}
	}
	for(rll i=0;i<ans.size();i++)
	{
		for(rll j=0;j<3;j++) write(h[ans[i]][j]),put_;
		putn;
	}
	return 0;
}
posted @ 2022-09-27 21:05  1Liu  阅读(54)  评论(0编辑  收藏  举报