北京Day 4

昨天rank1(假),今天颓废日,自闭了。

四道题,满分408,我的分刚赶上大佬的零头,血亏一百多分,两道题一共得五分,后面还玩啥。

本来都不想写博客了,然而老师后来把数据和题解都发下来了,所以虽然很颓,还是要贴题解。

T1

题目大意:求1-n中所有数的约数异或和的异或和。n<=1e14。

考场上写的O(sqrt(n)*log(n))的做法,但是写挂了。其实可以不用那个log。

考虑每个约数对答案的贡献,发现只有出现奇数次才会被统计,而出现次数为n/i,取值一共有(sqrt(n))个,对于连续的一串数,由于(4x)xor(4x+1)xor(4x+2)xor(4x+3)=0,它们的异或和有如下性质:i=k(k%4==0),1(k%4==1),k+1(k%4==2),0(k%4==3)。

这样我们就可以O(1)处理每次更新答案。

T1AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
#pragma GCC optimize(2)
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
long long n,ans,y,las1,las2;
inline long long solve(long long l)
{
	int v=l&3;
	if(!v)return l;
	if(v==1)return 1;
	if(v==2)return l+1;
	return 0;
}
int  main()
{
	cin>>n;las1=n;
	y=(int)sqrt(n);
	for(register int i=1;i<y;++i)
	{las2=n/i;
	if(las2&1)ans^=i;
	if((i-1)&1) ans^=solve(las2)^solve(las1);
	las1=las2;
	}
	for(register int i=y;i<=las1;++i){if((n/i)&1)ans^=i;}
	cout<<ans<<endl;return 0;
}

T2

题目大意:给定长度为n的序列,m次操作,每次求[l,r]区间中满足不同的三个数进行或运算之后所得到的数等于x的三元组个数。

n,m<=100000。x,a[i]<=255.

考场上写的暴力,结果数组开大了,(不过不开大也只能过1个点)。

正解有两个:

一个是容斥,用前缀和预处理出l到r中a[i] or x == x的数的个数,答案就等于这个区间中所有i or x ==x 的数进行容斥,与x二进制下1的个数差为奇数的系数为-1,为偶数的系数为+1。

另外一个是莫队+fwt,用fwt快速计算异或和,设l到r中数字的序列为a,则答案等于(a^3-3a^2+2a)/6(序列次幂用fwt计算)。(然而并不会代码超短的fwt)

T2AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
#pragma GCC optimize(2)
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
void pu_t(long long x){if(x>9)pu_t(x/10);putchar(x%10+48);}
int n,m,a[100010],sum[260][100010],gs[260];
long long ans;
inline long long zhs(int x)
{return 1ll*x*(x-1)*(x-2)/6;}
int  main()
{
	register int l,r,x;
	n=re_ad();m=re_ad();
	for(int i=1;i<=n;++i)a[i]=re_ad();
	for(int i=1;i<=255;i++)gs[i]=gs[i>>1]+(i&1);
	for(int i=1;i<=n;i++)
	for(int j=0;j<=255;j++){sum[j][i]=sum[j][i-1];if((a[i]&j)==a[i])++sum[j][i];}
	while(m--)
	{
	l=re_ad()-1;r=re_ad();x=re_ad();ans=0;
	for(int i=0;i<=255;i++)
	{if((x&i)==i){if((gs[x]-gs[i])&1)ans-=zhs(sum[i][r]-sum[i][l]);else ans+=zhs(sum[i][r]-sum[i][l]);}
	}
	pu_t(ans);putchar('\n');
	}
}

T3

题目大意:给定n个点m条边的无向图,求该图是否存在两个无公共边的生成树(求该图是否存在一种划分边集的方法,使得划分的两组边集都可以使该图连通)。n<=10,m<=100。

考场上写了一个2^m的暴力,正解是神奇的搜索。

维护两张图,每次贪心连通块个数向两张图中加边,如果加边可以使两张图中的连通块都减少,则枚举加入哪张图,递归搜索即可。由于n极小,且每次分叉连通块个数都会--,故可以通过本题。

T3AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
#pragma GCC optimize(2)
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
int n,m,T;
struct node{
	int fa[30],val,tot,ret[1010];
}A,B;
struct e{int u,v;}f[1010];
int find(int x,node &a){return (x==a.fa[x])?x:(find(a.fa[x],a));}
inline void unite(int x,int y,node &a)
{int u=find(x,a);a.ret[++a.tot]=u;a.fa[u]=a.fa[find(y,a)];--a.val;}
inline void retur(node &a)
{a.fa[a.ret[a.tot]]=a.ret[a.tot];--a.tot;++a.val;}
bool dfs(int x)
{
	if((A.val==1)&&(B.val==1))return true;
	
	if(x>m)return false;
	bool fla=false;
	if(find(f[x].u,A)!=find(f[x].v,A))
	{unite(f[x].u,f[x].v,A);fla=true;if(dfs(x+1))return true;retur(A);}
	if(find(f[x].u,B)!=find(f[x].v,B))
	{unite(f[x].u,f[x].v,B);fla=true;if(dfs(x+1))return true;retur(B);}
	if(!fla)return dfs(x+1);
	return false;
}
int main()
{
	T=re_ad();
	while(T--)
	{
	n=re_ad();m=re_ad();
	for(int i=1;i<=m;i++){f[i].u=re_ad();f[i].v=re_ad();}
	A.val=n;B.val=n;//A.tot=0;B.tot=0;
	for(int i=1;i<=n+1;i++)A.fa[i]=i,B.fa[i]=i;
	if(dfs(1))printf("Possible\n");else printf("Impossible\n");
	}
	return 0;
}

T4

题目大意:有 m 家工厂,第 i 家工厂的零件出售单价为 pi,上市时间 为第di天。 另外有 n 家商店,第 i 家商店 的零件收购单价为 qi,最晚愿 意交易到第 ei天。你需要分别与一家工厂和一家商店签订合同,每天从 工厂购 入至多一件零件,并在当天将其出售给商店。求最大可能的获利。

题解:将工厂和商店分别按照对答案的贡献来排序,删除对答案无贡献的点,利用决策单调性,进行分治。 令 solve(l,r, dl, dr) 表示在 [l,r] 间选择商店,在 [dl, dr]  间选择工厂。取 mid = ⌊ (l+r)/ 2⌋, 取遍 [dl, dr] 间所有工厂,选取对商店 mid 最优的工厂 dm。 那么对于 [l, mid) 的商店,它们最优的工厂只能在 [dl, dm] 间选取。同理对于 (mid,r] 的 商店,它们最优的工厂只能 在 [dm, dr] 间选取。故 solve(l, mid − 1, dl, dm) , solve(mid + 1,r, dm, dr) 即可。 如果对于商店 mid 来说,并没有一家工厂使得它 们间 的获利为正,在一开始将这种商店直接剔除即可。

T4AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
#pragma GCC optimize(2)
using namespace std;
const int inf=2000000000;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
int n,m,cnt;
long long ans,an[500010];
struct nod{int p,d;}gc[500010];
struct node{int q,e;}sd[500010];
bool cmp(nod x,nod y){if(x.p!=y.p)return x.p<y.p;return x.d<y.d;}
bool comp(node x,node y){if(x.q!=y.q)return x.q>y.q;return x.e>y.e;}
void solve(int l,int r,int L,int R)
{
	if(l>r)return;
	int mid=(l+r)>>1,pos;
	long long t;
	for(int i=L;i<=R;i++)
	{
	if(sd[i].q<gc[mid].p&&sd[i].e<gc[mid].d)continue;
	t=1ll*(sd[i].q-gc[mid].p)*(sd[i].e-gc[mid].d);
	if(an[mid]<t)an[mid]=t,pos=i;
	}
	ans=max(ans,an[mid]);
	solve(l,mid-1,L,pos);solve(mid+1,r,pos,R);
}
int  main()
{
	register int i,no,now;
	m=re_ad();n=re_ad();
	for(int i=1;i<=m;i++)gc[i].p=re_ad(),gc[i].d=re_ad();
	for(int i=1;i<=n;i++)sd[i].q=re_ad(),sd[i].e=re_ad();
	sort(gc+1,gc+m+1,cmp);
	now=inf;i=1;
	while(i<=m)
	{
	if(gc[i].d<now){an[cnt]=-1ll*inf*inf;gc[++cnt]=gc[i];now=gc[i].d;}
	no=gc[i].p;
	while((i<=m)&&(gc[i].p==no))++i;
	}
	m=cnt;cnt=0;
	sort(sd+1,sd+n+1,comp);
	now=-inf;i=1;
	while(i<=n)
	{
	if(sd[i].e>now){sd[++cnt]=sd[i];
	now=sd[i].e;}
	no=sd[i].q;
	while(i<=n&&sd[i].q==no)++i;
	}
	n=cnt;cnt=0;
	for(int i=1;i<=n/2;i++)swap(sd[i],sd[n-i+1]);
	solve(1,m,1,n);
	cout<<ans<<endl;return 0;
}

总结

啥也不说了,好好学习。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值