加载中…

返回上一页

CSP-S模拟10

下发文件和题解

A. 欧几里得的噩梦

求异或和首先就要想到线性基,每加入一个数就相当于模拟把一个数插入线性基.

对于每个输入的数,可以使用并查集维护能两两异或的所有值. 在 1 的个数为 2 时,把 xy 合并. 为 1 时,把 x 和一个超级源点(下面记为 m+1)合并. 在能合并时,记录一下这个值,然后答案 +1. 如果已经合并过直接跳过就行了.

记答案为 ans. 由于每个数在二进制下两两不同,因此异或值也均不相同. 所以能够得到的数字数量就是 2ans.

要求子集的字典序最小,那么就从 1 开始,只要记录过就输出,直至输出了 ans 个.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define sinline static inline
#define rll rg ll
#define maxn 2000001
#define mod 1000000007
#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,m,x,y,ans;
ll f[maxn];
bool vis[maxn];
sinline ll find(rll x) { if(x!=f[x]) f[x]=find(f[x]); return f[x]; }
sinline ll ksm(rll a,rll b) { rll ans=1; a%=mod; for(rll i=b;i;i>>=1) { if(i&1) ans=ans*a%mod; a=a*a%mod; } return ans; }
int main()
{
	for(rll i=1;i<maxn;i++) f[i]=i;
	n=read();m=read();
	for(rll i=1;i<=n;i++)
	{
		if(!(read()^1))
		{
			x=read();if(find(x)!=find(m+1)) ans++,vis[i]=1,f[find(x)]=find(m+1);
		}
		else
		{
			x=read();y=read();
			if(find(x)!=find(y)) ans++,vis[i]=1,f[find(x)]=find(y);
		}
	}
	write(ksm(2,ans));put_;write(ans);putn;
	for(rll i=1,sum=0;i<=n;i++) { if(vis[i]) sum++,write(i),put_; if(sum==ans) break; }
	return 0;
}

B. 清扫

首先,对于只有两个点的情况,如果两个点的剩余次数不同,那么一定不能消去. 特判一下即可.(后面用到的方法会与之冲突)

f[i] 表示以节点 i任意一个节点,只要有子树即可)为,它的子树的剩余石子数(剩余次数).

设该子树内进行了 x 次清除操作,那么其子树的剩余次数(f[i]) 就减少了 2x(因为有两端要乘 2),它自己剩余的次数即为 a[i] - x.

移项,有:

x = f[i] - a[i].

剩余的点数就是:

a[i] - x = 2a[i] - f[i].

这样就能够转移了.

下面判是否合法:

  1. f[i] < a[i] 时,所有叶子节点的次数用完了,但是根仍剩余次数,一定是无法消去的,不合法.

  2. newf[i] = 2a[i] - f[i]mx为当前点所有子树中剩余次数最多的点的剩余次数. 当 newf[i] < 0newf[i] < 2mx - f[x] 时,剩余次数最多的那个子树,它的剩余次数比其余的都多,所有的都消完了,它消不掉,也不合法.

  3. 最重转移完以后 f[i] 有剩余,未消完,也不合法.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define sinline static inline
#define rll rg ll
#define maxn 2000001
#define mod 1000000007
#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],f[maxn];
vector<ll> g[maxn];
sinline bool dfs(rll x,rll fa)
{
	if(g[x].size()==1) { f[x]=a[x]; return 1; } rll mx=0;
	for(rll i=0;i<g[x].size();i++)
	{
		rll to=g[x][i];if(to==fa) continue;
		if(!dfs(to,x)) return 0;f[x]+=f[to];mx=max(mx,f[to]);
	}
	if(f[x]<a[x]) return 0;// 当前点为根,它的子树所有剩余次数都消不掉它,显然不合法.
	rll k=(mx<<1)-f[x];f[x]=(a[x]<<1)-f[x];
	return f[x]>=max(k,(ll)0);// 剩余次数最多的那个子树,它的剩余次数比其余的都多,所有的都消完了,它消不掉,也不合法.
}
int main()
{
	n=read();for(rll i=1;i<=n;i++) a[i]=read();if(n==2) { puts(a[1]^a[2]?"NO":"YES"); return 0; }
	for(rll i=1,u,v;i<n;i++) u=read(),v=read(),g[u].push_back(v),g[v].push_back(u);
	for(rll i=1;i<=n;i++) if(g[i].size()>1) { puts(dfs(i,0)&&!f[i]?"YES":"NO"); break; }
	return 0;
}

C. 购物

签到题,然而我并没有签到成功.

遇到求总数的题,要先化繁为简,考虑单个 k 是如何能够被选中的.

在序列中如果存在 [k,2k] 的值那一定可以,要不然把序列排好序,小于 k 的数之和大于 k 也可以(想一想,为什么?).

那么可以求出前缀和,把这 n 个数和 n 个前缀和枚举一遍,求出可以的 k 的区间,求一下并集就可以了.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define sinline static inline
#define rll rg ll
#define inf 0x3f3f3f3f3f3f3f3f
#define maxn 1000001
#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 l,r;
	inline friend bool operator<(rg node a,rg node b) { if(a.l==b.l) return a.r<b.r; return a.l<b.l; }
}b[maxn];
ll n,mx,cnt,ans;
ll a[maxn],sum[maxn];
int main()
{
	n=read();for(rll i=1;i<=n;i++) a[i]=read();
	sort(a+1,a+n+1);for(rll i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
	for(rll i=1;i<=n;i++)
		b[++cnt]=(node){(a[i]>>1)+(a[i]&1),a[i]};
	for(rll i=1;i<=n;i++)
		b[++cnt]=(node){(sum[i]>>1)+(sum[i]&1),sum[i]};
	sort(b+1,b+cnt+1);rll l=0,r=0;
	for(rll i=1;i<=cnt;i++)
	{
		if(b[i].l>r) { l=b[i].l,r=b[i].r; ans+=r-l+1; continue; }
		ans+=b[i].r-r;r=b[i].r;
	}
	write(ans);
	return 0;
}

D. ants

回滚莫队模板题.

由于我打了半天忘记保存然后电脑给关机了,不想打了……

点击查看代码
#include<bits/stdc++.h>
#define ll int
#define rg register
#define sinline static inline
#define rll rg ll
#define maxn 500001
#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 nd { ll op,pos,v; };
ll n,m,k,len,nowr;
ll a[maxn],bel[maxn],ans[maxn];
ll sum,tsum,lenl[maxn],lenr[maxn];
stack<nd> s;
struct node
{
	ll l,r,id;
	inline friend bool operator<(rg node a,rg node b) { if(bel[a.l]==bel[b.l]) return a.r<b.r; return bel[a.l]<bel[b.l]; }
}q[maxn];
int main()
{
	n=read();m=read();for(rll i=1;i<=n;i++) a[i]=read();for(rll i=1;i<=m;i++) q[i]=(node){read(),read(),i};
	len=sqrt(n);for(rll i=1;i<=n;i++) bel[i]=i/len+1;sort(q+1,q+m+1);
	for(rll i=1;i<=m;i++)
	{
		if(bel[q[i].l]^bel[q[i-1].l])
		{
			sum=0; for(rll i=1;i<=n;i++) lenl[i]=lenr[i]=0;
			nowr=len*bel[q[i].l];
		}
		while(q[i].r>nowr)
		{
			nowr++;
			lenl[a[nowr]]=lenl[a[nowr]-1]+1;lenr[a[nowr]]=lenr[a[nowr]+1]+1;
			k=lenl[a[nowr]]+lenr[a[nowr]]-1;sum=max(sum,k);
			lenl[a[nowr]+lenr[a[nowr]]-1]=lenr[a[nowr]-lenl[a[nowr]]+1]=k;
		}
		tsum=sum;
		for(rll nowl=q[i].l;nowl<=min(bel[q[i].l]*len,q[i].r);nowl++)
		{
			lenl[a[nowl]]=lenl[a[nowl]-1]+1;lenr[a[nowl]]=lenr[a[nowl]+1]+1;
			k=lenl[a[nowl]]+lenr[a[nowl]]-1;
			s.push((nd) { 0,a[nowl]+lenr[a[nowl]]-1,lenl[a[nowl]+lenr[a[nowl]]-1] });
			s.push((nd) { 1,a[nowl]-lenl[a[nowl]]+1,lenr[a[nowl]-lenl[a[nowl]]+1] });
			lenl[a[nowl]+lenr[a[nowl]]-1]=lenr[a[nowl]-lenl[a[nowl]]+1]=k;tsum=max(tsum,k);
		}
		while(!s.empty())
			if(!s.top().op) lenl[s.top().pos]=s.top().v,s.pop();
			else lenr[s.top().pos]=s.top().v,s.pop();
		for(rll nowl=q[i].l;nowl<=min(bel[q[i].l]*len,q[i].r);nowl++) lenl[a[nowl]]=lenr[a[nowl]]=0;
		ans[q[i].id]=tsum;
	}
	for(rll i=1;i<=m;i++) write(ans[i]),putn;
	return 0;
}
posted @ 2022-09-24 20:15  1Liu  阅读(32)  评论(0)    收藏  举报