AtCoder Grand Contest 045
A - Xor Battle
可以发现,从后往前扫,遇到一个
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=205;
int T;
int n;
long long a[N];
char s[N];
struct Basis
{
static const int M=60;
long long d[N];
void clear()
{
memset(d,0,sizeof(d));
return;
}
void insert(long long t)
{
for(int i=M;i>=0;i--)
if(t&(1LL<<i))
{
if(d[i]) t^=d[i];
else
{
for(int j=0;j<i;j++)
if(t&(1LL<<j)) t^=d[j];
for(int j=i+1;j<=M;j++)
if(d[j]&(1LL<<i)) d[j]^=t;
d[i]=t;
break;
}
}
return;
}
bool find(long long t)
{
for(int i=M;i>=0;i--)
if(t&(1LL<<i))
{
if(d[i]) t^=d[i];
else return false;
}
return true;
}
}lb;
void solve()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
scanf("%s",s+1);
lb.clear();
for(int i=n;i>=1;i--)
if(s[i]=='1')
{
if(!lb.find(a[i]))
{
printf("1\n");
return;
}
}
else if(s[i]=='0') lb.insert(a[i]);
printf("0\n");
return;
}
int main()
{
scanf("%d",&T);
while(T--)
solve();
return 0;
}
B - 01 Unbalanced
不妨先将所有的 ?
填成 1
,最后再调整。
把 1
看成 0
看成
考虑枚举 ?
改成 0
,对
令
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000005;
const int INF=1061109567;
int n;
char s[N];
int a[N];
int sum[N];
int suf[N];
int calc(int Min)
{
static int b[N];
int add=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='?'&&suf[i]-add-2>=Min) add+=2;
b[i]=sum[i]-add;
}
int Max=max(*max_element(b+1,b+n+1),0);
return Max-Min;
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++)
if(s[i]=='1'||s[i]=='?') a[i]=1;
else if(s[i]=='0') a[i]=-1;
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+a[i];
suf[n+1]=INF;
for(int i=n;i>=1;i--)
suf[i]=min(suf[i+1],sum[i]);
int m=min(*min_element(sum+1,sum+n+1),0);
printf("%d",min(calc(m),calc(m-1)));
return 0;
}
C - Range Set
不妨令
考虑一个最后的字符串是否能被达到,可以将所有的长度
合法的方案很难算,不妨计算不合法的方案。
令
令
注意最后的长度
代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=5005;
const int MOD=1000000007;
int n,a,b;
long long f[N][2];
long long g[N];
long long dp[N][2];
long long ksm(long long a,long long b)
{
long long res=1;
while(b)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD,b>>=1;
}
return res;
}
int main()
{
scanf("%d%d%d",&n,&a,&b);
if(a>b) swap(a,b);
if(b==1)
{
printf("%lld",ksm(2,n));
return 0;
}
f[0][0]=f[0][1]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<i;j++)
f[i][1]=(f[i][1]+f[j][0])%MOD;
for(int j=0;j+a<=i;j++)
f[i][0]=(f[i][0]+f[j][1])%MOD;
}
g[0]=1;
for(int i=1;i<=n;i++)
g[i]=(f[i][0]+f[i][1])%MOD;
long long ans=0;
for(int i=1;i<n;i++)
{
if(i<a) dp[i][0]=(dp[i][0]+1)%MOD;
for(int j=max(i-a+1,1);j<i;j++)
dp[i][0]=(dp[i][0]+dp[j][1])%MOD;
if(i<b) dp[i][1]=(dp[i][1]+g[i-1])%MOD;
for(int j=max(i-b+1,1);j<i;j++)
dp[i][1]=(dp[i][1]+dp[j][0]*(i-j==1?1:g[i-j-2])%MOD)%MOD;
if(i+b-1>=n) ans=(ans+dp[i][0]*g[n-i-1]%MOD)%MOD;
if(i+a-1>=n) ans=(ans+dp[i][1])%MOD;
}
printf("%lld",(ksm(2,n)-ans+MOD)%MOD);
return 0;
}
D - Lamps and Buttons
可以发现,不合法的情况为环中一个点都没有被点亮,或者有个自环他去点了。
考虑枚举第一个
对于
的点,可以将这些点去掉。 的点,记为 个。 的点。记为 个。每个点所在环中都要至少存在一个小于 的点。 的点。记为 个。
每次考虑依次插入每种点,计算方案数,最后乘一个容斥系数就好了。
代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=10000005,M=5005;
const int MOD=1000000007;
int n,m;
long long ksm(long long a,long long b)
{
long long res=1;
while(b)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD,b>>=1;
}
return res;
}
long long fac[N],inv[N];
void init(int n=10000000)
{
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%MOD;
inv[n]=ksm(fac[n],MOD-2);
for(int i=n;i>=1;i--)
inv[i-1]=inv[i]*i%MOD;
return;
}
long long C(int n,int m)
{
if(m>n) return 0;
else return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int main()
{
scanf("%d%d",&n,&m);
init();
long long ans=0;
for(int i=1;i<=m+1;i++)
for(int j=0;j<i;j++)
{
int a=i-1-j,b=n-m,c=max(m-i,0);
long long res=C(i-1,j);
res=(res*fac[a])%MOD;
res=(res*fac[a+b-1]%MOD*inv[a-1]%MOD)%MOD;
res=(res*fac[a+b+c]%MOD*inv[a+b]%MOD)%MOD;
if(j&1) ans=(ans-res+MOD)%MOD;
else ans=(ans+res)%MOD;
}
printf("%lld",ans);
return 0;
}
E - Fragile Balls
考虑
- 只包含一个自环;
- 只包含一个长度大于等于
的简单环; - 联通块的边数大于点数。
当存在第 2 种联通块的话,答案就是
考虑
考虑从外面移入一个球
在第 1 种联通块内,对联通块数贡献为 ,需要多花费两步。 在第 2 种联通块内,对联通块数贡献为 ,不需要多花费步数。 在第 3 种联通块内,且 ,对联通块数贡献为 ,需要多花费一步。 在第 3 种联通块内,且 ,对联通块数贡献为 ,不需要多花费步数。
对于 1 和 2,我们需要一个 3 或者 4 先移动进去,然后再操作。显然不需要多花费步数的都可以取。
接下来就只有 1 和 3 了,考虑枚举 1 的个数,然后 two-pointer 维护就好了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=1000005;
const long long INF=4557430888798830399;
int n,m;
int a[N],b[N],c[N];
int fa[N];
int getf(int v)
{
if(v==fa[v]) return v;
fa[v]=getf(fa[v]);
return fa[v];
}
void merge(int u,int v)
{
int f1=getf(u),f2=getf(v);
if(f1!=f2) fa[f2]=f1;
return;
}
int in[N],siz[N];
bool circle[N];
vector<int>pos[4];
long long s2[N],s0[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
fa[i]=i;
long long ans=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i],&b[i],&c[i]);
if(a[i]!=b[i]) ans++;
merge(a[i],b[i]);
in[a[i]]++;
}
memset(circle,true,sizeof(circle));
for(int i=1;i<=n;i++)
{
siz[getf(i)]++;
if(in[i]>1) circle[getf(i)]=false;
}
int cnt=0;
for(int i=1;i<=n;i++)
if(getf(i)==i)
{
if(circle[i]&&siz[i]>1) cnt++;
}
if(cnt==0)
{
printf("%lld",ans);
return 0;
}
ans+=cnt;
for(int i=1;i<=m;i++)
if(c[i]>=2)
{
int u=getf(a[i]);
if(circle[u])
{
if(siz[u]==1) pos[0].push_back(c[i]-2);
else if(siz[u]>1) pos[1].push_back(c[i]-1);
}
else
{
if(a[i]==b[i]) pos[2].push_back(c[i]-1);
else pos[3].push_back(c[i]-1);
}
}
for(int p=0;p<=3;p++)
sort(pos[p].begin(),pos[p].end(),greater<int>());
long long sum=0;
for(int p=0;p<=3;p++)
for(int c:pos[p])
sum+=c;
if(sum<cnt)
{
printf("-1");
return 0;
}
if(pos[2].empty()&&pos[3].empty())
{
printf("-1");
return 0;
}
if(pos[3].empty()) ans++,cnt-=pos[2].front(),pos[2].erase(pos[2].begin());
else cnt-=pos[3].front(),pos[3].erase(pos[3].begin());
for(int c:pos[3])
cnt-=c;
for(int c:pos[1])
cnt-=c;
pos[3].clear();
pos[1].clear();
if(cnt<=0)
{
printf("%lld",ans);
return 0;
}
for(int i=1;i<=pos[0].size();i++)
s0[i]=s0[i-1]+pos[0][i-1];
for(int i=1;i<=pos[2].size();i++)
s2[i]=s2[i-1]+pos[2][i-1];
long long res=INF;
for(int i=pos[0].size(),j=0;i>=0;i--)
{
while(j<=pos[2].size()&&s0[i]+s2[j]<cnt) j++;
if(j<=pos[2].size()) res=min(res,2LL*i+j);
}
printf("%lld",ans+res);
return 0;
}
F - Division into Multiples
首先可以将
考虑每组
考虑计算出可能成为答案的那些解
考虑一个高为
不妨令
观察上面的算法,将
因为每次矩形的高一定会越来越小,故一定满足
将所有的
可以发现,肯定选两种相邻或相同的
那么就可以二分答案,对于每天线段判断是否可以选
将两式转化一下:
将相加,可得:
代码:
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
int T;
int ex_gcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
int d=ex_gcd(b,a%b,x,y);
int tmp=x;
x=y,y=tmp-a/b*y;
return d;
}
int getinv(int A,int MOD)
{
int x,y;
int d=ex_gcd(A,MOD,x,y);
if(d!=1) return -1;
return (x+MOD)%MOD;
}
int a,x,b,y,c;
int d;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
vector<pair<int,int> >pos;
vector<int>cnt;
void getpos()
{
pos.clear(),cnt.clear();
int W=c,H=d;
int now=0;
while(true)
{
int r=W/H;
pos.push_back(make_pair(1LL*now*getinv(d,c)%c,now%c==0?c:(c-now+c)%c)),cnt.push_back(r);
now+=r*H;
W%=H;
if(W==0) break;
H%=W;
if(H==0) H=W;
}
pos.push_back(make_pair(c,0));
return;
}
void solve()
{
scanf("%d%d%d%d%d",&a,&x,&b,&y,&c);
int G;
G=gcd(a,b),a/=G,b/=G,c/=gcd(G,c);
G=gcd(a,c),a/=G,c/=G,y/=G;
G=gcd(b,c),b/=G,c/=G,x/=G;
a%=c,b%=c;
if(c==1)
{
printf("%d\n",x+y);
return;
}
d=1LL*a*getinv(b,c)%c;
getpos();
int ans=0;
for(size_t i=0;i<cnt.size();i++)
{
int xl=pos[i].first,yl=pos[i].second,xr=pos[i+1].first,yr=pos[i+1].second;
int dx=(xr-xl)/cnt[i],dy=(yl-yr)/cnt[i];
int l=0,r=x+y,res=0;
while(l<=r)
{
int mid=((long long)l+r)/2;
auto check=[=](int mid)
{
long long p=(x-1LL*xl*mid)/dx,q=(y-1LL*yr*mid)/dy;
return (x-1LL*xl*mid)>=0&&(y-1LL*yr*mid)>=0&&p+q>=1LL*cnt[i]*mid;
};
if(check(mid)) res=mid,l=mid+1;
else r=mid-1;
}
ans=max(ans,res);
}
printf("%d\n",ans);
return;
}
int main()
{
scanf("%d",&T);
while(T--)
solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通