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;
}