CSP-S模拟10
下发文件和题解
A. 欧几里得的噩梦
求异或和首先就要想到线性基,每加入一个数就相当于模拟把一个数插入线性基.
对于每个输入的数,可以使用并查集维护能两两异或的所有值. 在 1 的个数为 2 时,把 x 与 y 合并. 为 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].
这样就能够转移了.
下面判是否合法:
-
当 f[i] < a[i] 时,所有叶子节点的次数用完了,但是根仍剩余次数,一定是无法消去的,不合法.
-
设 newf[i] = 2a[i] - f[i],mx为当前点所有子树中剩余次数最多的点的剩余次数. 当 newf[i] < 0 或 newf[i] < 2mx - f[x] 时,剩余次数最多的那个子树,它的剩余次数比其余的都多,所有的都消完了,它消不掉,也不合法.
-
最重转移完以后 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;
}
--END--

浙公网安备 33010602011771号
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/p/16726402.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!