20240708比賽總結

wangsiqi2010916發表於2024-07-09

T1 分糖果

https://gxyzoj.com/d/hzoj/p/3752

因為是三的倍數,所以按餘數分為三種情況,分別是:3個0,3個1,3個2,012

顯然,當012的組數超過2時,就會出現3組相同餘數的,所以列舉012的組數即可

程式碼:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,a[100005],cnt[3],b[3][100005],ans,p;
int main()
{
//	freopen("data19.in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		a[i]%=3;
		b[a[i]][++cnt[a[i]]]=i;
	}
	ans=0;
	for(int i=0;i<3;i++)
	{
		int x=cnt[0]-i,y=cnt[1]-i,z=cnt[2]-i;
		int tmp=x/3+y/3+z/3+i;
		if(tmp>ans)
		{
			ans=tmp,p=i;
		}
	}
	printf("%d\n",ans);
	for(int i=0;i<3;i++)
	{
		for(int j=cnt[i]-2;j>p;j-=3)
		{
			printf("%d %d %d\n",b[i][j+2],b[i][j+1],b[i][j]);
		}
	}
	for(int i=1;i<=p;i++)
	{
		printf("%d %d %d\n",b[0][i],b[1][i],b[2][i]);
	}
	return 0;
}

T2 乒乓球

https://gxyzoj.com/d/hzoj/p/3753

如果兩個人的比分相等且大於11,那與11:11等價

如果兩個人的比分相差1且大於11,那與11:10等價

所以可以迴圈列舉每一個週期,記錄在這個週期結束時的兩人的比分,如果重複出現,則找到了大週期

在這個過程中,可以記錄每個人贏的次數

此時,可以直接算出在n個球中屬於大迴圈的所有球的勝負情況,剩餘暴力列舉即可

程式碼:


#include<cstdio>
#include<string>
#include<iostream>
#define ll long long
using namespace std;
ll n,sa[15][15],sb[15][15],t[15][15],k;
string s;
ll a=0,b=0,cnta=0,cntb=0;
int main()
{
//	freopen("data06.in","r",stdin);
	scanf("%lld%lld",&n,&k);
	cin>>s;
	for(int i=0;i<12;i++)
	{
		for(int j=0;j<12;j++)
		{
			sa[i][j]=sb[i][j]=t[i][j]=-1;
		}
	}
	sa[0][0]=sb[0][0]=t[0][0]=0;
	ll tmp=0;
	while(tmp*k<=n)
	{
		for(int i=1;i<=k;i++)
		{
			if(s[i-1]=='A') a++;
			else b++;
			if(a>=11&&a-b>=2)
			{
				cnta++,a=b=0;
			}
			if(b>=11&&b-a>=2)
			{
				cntb++,a=b=0;
			}
			if(a==b&&a>=11) a=b=11;
			if(a>=11&&a-b==1) a=11,b=10;
			if(b>=11&&b-a==1) a=10,b=11; 
		}
		tmp++;
		if(t[a][b]!=-1) break;
		t[a][b]=tmp,sa[a][b]=cnta,sb[a][b]=cntb;
	//	printf("%d %d %d %d\n",a,b,cnta,cntb);
	}
	ll tim=(tmp-t[a][b])*k,A=cnta-sa[a][b],B=cntb-sb[a][b];
	if(tim)
	{
		ll tmp1=(n-t[a][b]*k)/tim;
		A=A*tmp1+sa[a][b],B=B*tmp1+sb[a][b];
		tmp=n-t[a][b]*k-tim*tmp1;
		tmp=n-tmp;
	}
	else tmp=0;
	//	printf("%lld ",tmp);
	for(ll i=tmp;i<n;i++)
	{
		int p=i%k;
		if(s[p]=='A') a++;
		else b++;
		if(a>=11&&a-b>=2)
		{
			A++,a=b=0;
		}
		if(b>=11&&b-a>=2)
		{
			B++,a=b=0;
		}
		if(a==b&&a>11) a=b=11;
	}
	printf("%lld:%lld",A,B);
	return 0;
}

T3 與或

https://gxyzoj.com/d/hzoj/p/3754

顯然,將所有的&放在最前面值最優的,但是要求字典序最小,所以考慮先算出理論最大值,然後進行列舉

假如當前放|不會影響結果,就放,但是如何check

顯然,直接列舉會T,考慮字首和,透過記錄每一位上1的個數,從而得到最終結果

程式碼:

#include<cstdio>
#define ll long long
using namespace std;
int n,k;
ll sum[200001][61],cnt[65],a[200005];
ll check(int st,ll now,int k1)
{
	if(st<=n-k1)
	{
		for(int i=0;i<60;i++) cnt[i]=0;
		for(int i=0;i<60;i++) cnt[i]=sum[n-k1][i]-sum[st-1][i];
		for(int i=0;i<60;i++)
		{
			if(cnt[i]!=n-k1-st+1&&((now>>i)&1))
			{
				now^=(1ll<<i);
			}
		}
	}
	if(n-k1+1<=n)
	{
		for(int i=0;i<60;i++) cnt[i]=0;
		for(int i=0;i<60;i++) cnt[i]=sum[n][i]-sum[n-k1][i];
		for(int i=0;i<60;i++)
		{
			if(cnt[i])
			{
				now|=(1ll<<i);
			}
		}
	}
	return now;
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		for(int j=0;j<60;j++)
		{
			sum[i][j]=sum[i-1][j];
			if((a[i]>>j)&1) sum[i][j]++;
		}
	}
	ll ans=check(2,a[1],k);
	printf("%lld\n",ans);
	ll nowk=k,tmp=a[1];
	for(int i=2;i<=n;i++)
	{
	//	printf("%d\n",check(i+1,a[i]|tmp,nowk-1));
		if(nowk>0&&check(i+1,a[i]|tmp,nowk-1)==ans)
		{
			printf("|");
			tmp=(a[i]|tmp);
			nowk--;
		}
		else
		{
			printf("&");
			tmp=(a[i]&tmp);
		}
	}
	return 0;
}