Codeforces Global Round 2

Codeforces Global Round 2

因为省选所以没打
orz打了的1tst上红了,成为了全机房唯一制定GM。

A. Ilya and a Colorful Walk

给你一个数列,问任意两个不同元素的距离的最大值是多少。

这个随便做吧。。

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 300300
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
vector<int> E[MAX];int n,ans;
int main()
{
	n=read();
	for(int i=1;i<=n;++i)E[read()].push_back(i);
	for(int i=1,mn=1e9,mx=0;i<=n;++i)
	{
		for(int v:E[i])
		{
			if(mn<1e9)ans=max(ans,abs(mn-v));
			if(mx)ans=max(ans,abs(mx-v));
		}
		for(int v:E[i])mn=min(mn,v),mx=max(mx,v);
	}
	printf("%d\n",ans);
	return 0;
}

B. Alyona and a Narrow Fridge

自己看题目吧,太难翻译了。。。

二分答案,然后贪心就可以了。。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 1010
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,H,a[MAX],b[MAX];
bool check(int m)
{
	for(int i=1;i<=m;++i)b[i]=a[i];
	sort(&b[1],&b[m+1]);reverse(&b[1],&b[m+1]);
	ll ret=0;b[m+1]=0;
	for(int i=1;i<=m;i+=2)ret+=max(b[i],b[i+1]);
	return ret<=H;
}
int main()
{
	n=read();H=read();
	for(int i=1;i<=n;++i)a[i]=read();
	int l=1,r=n,ret=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))l=mid+1,ret=mid;
		else r=mid-1;
	}
	printf("%d\n",ret);
	return 0;
}

C. Ramesses and Corner Inversion

给定一个\(01\)矩阵,每次可以将一个正方形四个角的元素取反,问能否从矩阵\(A\)变成矩阵\(B\)

首先问题可以转化成给定一个\(01\)矩阵要把它变成全\(0\)
接着发现任何一个正方形的操作都可以转化成若干个\(2*2\)的正方形的操作。
那么直接扫一遍就行了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 505
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,m,a[MAX][MAX],b[MAX][MAX];
int main()
{
	n=read();m=read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)a[i][j]=read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)a[i][j]^=read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
		{
			int c=b[i-1][j-1]^b[i][j-1]^b[i-1][j]^b[i][j]^a[i][j];
			if((i==n||j==m)&&c){puts("No");return 0;}
			b[i][j]^=c;
		}
	puts("Yes");
	return 0;
}

D. Frets On Fire

给你\(n\)个数\(a_i\),每次询问集合\(\{a_i+x,x\in[l,r]\}\)的大小。

\(a_i\)排序,显然\(x\in[l,r]\)等价于\(x\in[1,r-l+1]\),令\(k=r-l+1\)
然后问题就变成了,假设相邻两个数的差是\(p\),求\(\sum \min\{p,k\}\),那么差分之后排序每次直接二分就可以了。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 100100
const ll inf=1e18;
inline ll read()
{
	ll x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,Q;ll a[MAX],b[MAX];
int main()
{
	n=read();
	for(int i=1;i<=n;++i)a[i]=read();
	sort(&a[1],&a[n+1]);a[n+1]=5e18;
	for(int i=1;i<=n;++i)b[i]=a[i+1]-a[i];
	sort(&b[1],&b[n+1]);
	for(int i=1;i<=n;++i)a[i]=a[i-1]+b[i];
	Q=read();
	while(Q--)
	{
		ll l=read(),r=read(),k=r-l+1;
		int p=lower_bound(&b[1],&b[n],k)-b-1;
		ll ans=a[p]+(n-p)*k;
		printf("%lld ",ans);
	}
	return 0;
}

E. Pavel and Triangles

给你若干根长度为\(2^n\)的木棍,你每次可以选三根出来拼出一个三角形。
问最多可以拼出多少个三角形。

显然这个操作需要两根相等的木棍以及一个长度不超过这两根的木棍。
那么从小往大贪心就行了。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 300300
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n;ll ans,s;
int main()
{
	n=read();
	for(int i=1;i<=n;++i)
	{
		int x=read();
		if(s>=x/2)
		{
			if((x&1)&&x>3)x-=3,ans+=1;
			s-=x/2,ans+=x/2,s+=x&1;
		}
		else ans+=s,x-=s*2,ans+=x/3,x%=3,s=x;
	}
	printf("%lld\n",ans);
	return 0;
}

F. Niyaz and Small Degrees

给定一棵树,边有边权。
现在对于每个\(d\in[0,n)\),回答让每个顶点的度数都不超过\(d\)时,需要删掉的边权和的最小值。

假如只询问一次,假设度数的限制为\(d\)。我们可以设\(f[i][0/1]\)表示在当前以\(i\)号节点为根节点的子树中,所有点都满足度数限制,且\(i\)号点是否和其父亲相连的边是否被割掉时的最小代价。
转移的时候把所有儿子按照\(f[i][1]-f[i][0]+w_{i\rightarrow father}\)排序,选前度数限制小的就可以转移了。
发现对于\(d\)而言,我们只需要在所有的度数超过\(d\)的点构成的联通块上面\(dp\),因为如果一个点的度数已经小于了当前的度数限制,删边或者不删边对于这个点而已一定是没有影响的。
那么把度数从小往大处理,那么对于对于小于限制的可以直接丢掉,大于限制的直接\(dp\)转移,这样子每个点会被算度数次,而所有点的度数之和是\(O(n)\)级别的,这样子复杂度是没有问题的。
但是直接做会有问题,因为在转移的时候还是需要考虑到每一个儿子,这样子复杂度变成了\(O(n^2)\)级别。
我们拿一个\(set\)维护一个点的所有儿子,当一个点的度数小于当前限制的时候就直接加入进其他点的\(set\)里面去。然后对于不小于当前限制的点,那么就直接做树型\(dp\)就行了。
(记得要及时清空\(set\)
时间复杂度\(O(nlogn)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
#define ll long long
#define MAX 250250
#define pb push_back
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,dg[MAX];ll ans;
vector<int> D[MAX],DD[MAX];
struct Line{int v,w;};vector<Line> E[MAX];
bool operator<(Line a,Line b){return dg[a.v]<dg[b.v];}
inline void Add(int u,int v,int w){E[u].pb((Line){v,w});++dg[u];}
ll f[MAX][2],sum[MAX];
int Limit,h[MAX];
bool vis[MAX];
multiset<ll,greater<ll> > S[MAX];
void Del(int x,int l){while(S[x].size()>l)sum[x]-=*S[x].begin(),S[x].erase(S[x].begin());}
void Del(int x,int l,vector<ll> &p){while(S[x].size()>l)sum[x]-=*S[x].begin(),p.pb(*S[x].begin()),S[x].erase(S[x].begin());}
void dfs(int u,int ff)
{
	int m=dg[u]-Limit;ll tot=0;vis[u]=true;
	vector<ll> val1,val2;
	Del(u,max(0,m));
	while(h[u]<E[u].size()&&dg[E[u][h[u]].v]<=Limit)++h[u];
	for(int i=h[u];i<E[u].size();++i)
	{
		int v=E[u][i].v,w=E[u][i].w;
		if(vis[v]||ff==v)continue;
		dfs(v,u);
		if(f[v][1]+w<=f[v][0])--m,tot+=f[v][1]+w;
		else
		{
			tot+=f[v][0];ll ss=f[v][1]-f[v][0]+w;
			S[u].insert(ss);sum[u]+=ss;val2.pb(ss);
		}
	}
	Del(u,max(0,m),val1);
	f[u][0]=tot+sum[u];--m;
	Del(u,max(0,m),val1);
	f[u][1]=tot+sum[u];
	for(int i:val1)S[u].insert(i),sum[u]+=i;
	for(int i:val2)S[u].erase(S[u].find(i)),sum[u]-=i;	
}
int main()
{
	n=read();
	for(int i=1;i<n;++i)
	{
		int u=read(),v=read(),w=read();
		ans+=w;Add(u,v,w);Add(v,u,w);
	}
	for(int i=1;i<=n;++i)sort(E[i].begin(),E[i].end());
	for(int i=1;i<=n;++i)DD[dg[i]].pb(i);
	for(int i=1;i<=n;++i)for(int j=0;j<dg[i];++j)D[j].pb(i);
	printf("%lld ",ans);
	for(int i=1;i<n;++i)
	{
		Limit=i;ans=0;
		for(int u:DD[i])
			for(auto p:E[u])
			{
				int v=p.v,w=p.w;
				if(vis[v])continue;
				S[v].insert(w);sum[v]+=w;
			}
		for(int u:DD[i])vis[u]=true;
		for(int v:D[i])
		{
			if(vis[v])continue;
			dfs(v,0);ans+=f[v][0];
		}
		for(int v:D[i])vis[v]=false;
		printf("%lld ",ans);
	}
}

G. Get Ready for the Battle

你有\(n\)个士兵,你要把他们分成\(m\)组(可以分出空组),有\(m\)组敌人,第\(i\)组敌人的血量是\(a_i\),你每次要让\(m\)组每一组攻击一个敌人(可以相同),然后攻击的敌人的血量就会减少这一组的人数,当敌人的血量小于等于\(0\)之后就会嘶。
定义让\(m\)组都进行一次攻击为一轮操作,问最少多少轮可以把敌人全部打死。

神仙构造题。
首先发现答案的最小可能值是\(\frac{\sum a_i}{n}\)。即每个回合都恰好产生\(n\)的总伤害。
考虑这个值能否构造出来。要达到这个值,显然只有除最后一次之外,每一次都不能有浪费。
一个很沙雕的想法就是构建出每个敌人的血量模\(n\)的结果,然后把他们排序,相邻两个做差,这样子总和恰好是\(n\),但是这样子每个敌人就需要额外被一个前缀和给覆盖,那么最小的那个就要被使用\(n\)次,显然不符合我们的要求。
那么我们直接计算出前缀和模\(n\)的结果,排序之后再把相邻的两项做差,这样子我们每个数最终剩下来的余数一定是这个序列中的某一段,并且上一个数和下一个数的两端一定不交,且这两段是连续的。
那么我们只需要按照顺序扫一遍就可以啦。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 1001000
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,m,a[MAX],ans[MAX<<2],k[MAX],tot;ll s;
int main()
{
	n=read();m=read();
	for(int i=1,x;i<=m;++i)
	{
		a[i]=x=read();
		s+=x;k[i]=s%n;
	}
	k[m]=n;sort(&k[1],&k[m+1]);
	for(int i=m;i;--i)k[i]-=k[i-1];
	for(int i=1,j=1;i<=m;++i)
	{
		int x=a[i];
		while(x>0)ans[++tot]=i,x-=k[j],j=j%m+1;
	}
	while(tot%m)ans[++tot]=1;
	printf("%d\n",tot/m);
	for(int i=1;i<=m;++i)printf("%d ",k[i]);puts("");
	for(int i=1;i<=tot;++i)
	{
		printf("%d ",ans[i]);
		if(i%m==0)puts("");
	}
}

H. Triple

你有\(n\)个三元组\(\{a_i,b_i,c_i\}\),现在对于每个三元组变成一个由\(x\)\(a_i\)\(y\)\(b_i\)\(z\)\(c_i\)构成的可重集。
问从每个可重集中选择一个数出来使得他们异或和为\(t\in[0,2^k)\)的方案数。

显然每个三元组对应着唯一的一个值域数组,那么我们只需要把所有的数组全部\(FWT\)之后再乘起来就可以了。
然而这样子复杂度爆炸。
先把三个数处理一下,分别变成\(0,a_i\oplus b_i,a_i\oplus c_i\),这样子做异或卷积显然对于最终的答案是没有什么影响的(也就是再异或一下所有\(a\)的异或和)
于是每一个对应的值域数组变成了\(F_0=x,F_{a\oplus b}=y,F_{a\oplus c}=z\)
这样子做完转化之后,进行\(FWT\),发现每一位的取值只有四种,即\(x+y+z,x+y-z,x-y+z,x-y-z\)。(如果不处理的话就是\(8\)种)
那么我们对于每一位处理答案,考虑在\(n\)个数组中每一种情况分别出现了多少次。
假设上面的四种情况的出现次数分别是\(a,b,c,d\)次,那么首先我们知道\(a+b+c+d=n\),如果我们能够算出这四个值,那么最终就可以很容易的\(IFWT\)回去了。
那么就接着列方程(然而我并不会列),然后解解就行了。
这里有\(4\)个元,我们需要四个方程,首先第一个前面已经有了\(a+b+c+d=n\)
接下来我们考虑\(y\)的贡献,那么定义一个\(F\)数组,把所有\(a\oplus b\)在对应的位置上加一,然后再\(FWT\)。那么上面的四种情况在\(y\)的贡献下分别是\(1,1,-1,-1\),所以我们知道\(a+b-c-d=F_i\)。同理\(z\)的贡献,类似的,我们得到\(a-b+c-d=G_i\)
然而这样子还是少了一个方程,我们令\(H_i\)为把所有\(b\oplus c\)在对应位置上加了一再\(FWT\)之后的数组。我们知道\(xorFWT\)之后,假设只有一个位置上有一,假设这个位置是\(j\)。那么\(F_i\)位置的值是\((-1)^{pop\_count(i\&j)}\),假装这个值叫做\(C(i,j)\)。我们发现\(i\&(j\oplus k)\)的奇偶性和\(i\&j+i\&k\)是一样的,所以\(C(i,j\oplus k)=C(i,j)*C(i,k)\),所以我们可以得到方程\(a-b-c+z=H_i\)
然后手动解一下方程就可以算出每一位了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 100100
#define MOD 998244353
#define inv2 499122177
#define inv4 748683265
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
int n,K,N,F[1<<17],A[1<<17],B[1<<17],C[1<<17],tot;
int X,Y,Z,s1,s2,s3,s4;
void FWT(int *a,int opt)
{
	for(int i=1;i<N;i<<=1)
		for(int p=i<<1,j=0;j<N;j+=p)
			for(int k=0;k<i;++k)
			{
				int X=a[j+k],Y=a[i+j+k];
				a[j+k]=(X+Y)%MOD;a[i+j+k]=(X+MOD-Y)%MOD;
				if(opt==-1)a[j+k]=1ll*a[j+k]*inv2%MOD,a[i+j+k]=1ll*a[i+j+k]*inv2%MOD;
			}
}
int main()
{
	n=read();K=read();N=1<<K;
	X=read();Y=read();Z=read();
	for(int i=1;i<=n;++i)
	{
		int a=read(),b=read(),c=read();
		tot^=a;A[a^b]+=1;B[c^a]+=1;C[b^c]+=1;
	}
	s1=(0ll+X+Y+Z)%MOD;s2=(0ll+X+Y-Z+MOD)%MOD;
	s3=(0ll+X-Y+Z+MOD)%MOD;s4=(0ll+X-Y-Z+MOD+MOD)%MOD;
	FWT(A,1);FWT(B,1);FWT(C,1);
	for(int i=0;i<N;++i)
	{
		int x,y,z,w;
		x=1ll*(0ll+n+A[i]+B[i]+C[i])*inv4%MOD;
		y=(1ll*(n+A[i])*inv2%MOD-x+MOD)%MOD;
		z=(1ll*(n+B[i])*inv2%MOD-x+MOD)%MOD;
		w=(1ll*(n+C[i])*inv2%MOD-x+MOD)%MOD;
		F[i]=1ll*fpow(s1,x)*fpow(s2,y)%MOD*fpow(s3,z)%MOD*fpow(s4,w)%MOD;
	}
	FWT(F,-1);
	for(int i=0;i<N;++i)printf("%d ",F[i^tot]);puts("");
	return 0;
}
posted @ 2019-04-11 21:09  小蒟蒻yyb  阅读(345)  评论(0编辑  收藏  举报