Preface
断断续续打完了,难得的总分上了700
最后苟到了Rank16,下一场如果还能保持这样的话应该可以上7星了QAQ
Binary XOR
显然我们只需要求出最后异或完了之后(0)和(1)的个数即可
那么我们先求出(0)个数的上下界,然后枚举一下具体的个数,注意这个个数一定是两个两个变化的
方案数的话直接组合数算一下即可
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,mod=1e9+7;
int t,n,a0,a1,b0,b1,ans,L,R,fact[N],inv[N]; char s[N];
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
for (scanf("%d",&t),init(100000);t;--t)
{
RI i; scanf("%d",&n); ans=a0=a1=b0=b1=0;
for (scanf("%s",s+1),i=1;i<=n;++i) if (s[i]=='0') ++a0; else ++a1;
for (scanf("%s",s+1),i=1;i<=n;++i) if (s[i]=='0') ++b0; else ++b1;
for (L=n-min(a0,b0)-min(a1,b1),R=min(a0,b1)+min(a1,b0),i=L;i<=R;i+=2)
inc(ans,C(n,i)); printf("%d
",ans);
}
return 0;
}
Addition
首先研究下那段伪代码,很容易发现它的原理:先加起来(用(operatorname{xor}))然后把要进位的(用(operatorname{and}))留到下一次去加
那么答案就容易得出了,要连续操作就意味着要进行连续的进位,相当于是找出一段最长的以两个(1)开始的接下来全是(0,1)的长度
先注意对齐之后直接扫一遍即可
#include<cstdio>
#include<iostream>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,la,lb,ans; char a[N],b[N];
inline void expand(char *s,int& len,CI tar)
{
int dlt=tar-len; RI i; for (i=tar;i-dlt>0;--i) s[i]=s[i-dlt];
for (i=1;i<=dlt;++i) s[i]='0'; len=tar;
}
int main()
{
for (scanf("%d",&t);t;--t)
{
RI i,j; scanf("%s%s",a+1,b+1); la=strlen(a+1); lb=strlen(b+1);
if (lb==1&&b[1]=='0') { puts("0"); continue; }
int tp=max(la,lb)+1; expand(a,la,tp); expand(b,lb,tp);
for (ans=0,i=la;i;i=j)
{
if (a[i]!='1'||b[i]!='1') { j=i-1; continue; }
for (j=i-1;j&&a[j]!=b[j];--j); ans=max(ans,i-j);
}
printf("%d
",ans+1);
}
return 0;
}
Chefina and Ranges
本来搞了个很复杂的做法然后被陈指导嘲讽了QAQ
首先把区间变成左闭右开,然后离散化之后大力枚举分界点的位置(i),要满足(i)左边所有区间右端点(le i),右边所有区间左端点(>i),并且两边都要有至少一个区间
那么这个时候我们容易发现,只要枚举(i),范围是所有区间中最小的右端点(含)到最大的左端点(不含)
然后就是求跨过这个点的区间个数了,直接差分+前缀和即可
#include<cstdio>
#include<algorithm>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,INF=1e9;
int t,n,ans,l[N],r[N],L,R,rst[N<<1],num,dlt[N<<1];
inline int find(CI x)
{
return lower_bound(rst+1,rst+num+1,x)-rst;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d",&n),num=R=0,ans=L=INF,i=1;i<=n;++i)
{
scanf("%d%d",&l[i],&r[i]);
rst[++num]=l[i]; rst[++num]=r[i]; L=min(L,r[i]); R=max(R,l[i]);
}
if (L>R-1) { puts("-1"); continue; }
sort(rst+1,rst+num+1); num=unique(rst+1,rst+num+1)-rst-1;
for (i=1;i<=n;++i) ++dlt[find(l[i])],--dlt[find(r[i])];
for (i=1;i<=num;++i) dlt[i]+=dlt[i-1]; int lim=find(R);
for (i=find(L);i<lim;++i) ans=min(ans,dlt[i]);
for (printf("%d
",ans!=INF?ans:-1),i=1;i<=num;++i) dlt[i]=0;
}
return 0;
}
STICNOT
首先容易想到二分+贪心,每次变点权显然是把最小的(mid)个改成(infty)
然后考虑怎么check,下面提供一种方法:
将(a,b)从小到大排序,当且仅当任意(iin[1,n))都有(a_ige b_i),此时才存在一种构造方式符合题目要求
充分性画画图就证出来了,必要性留作自己思考
最后连树的形态都不用管……
#include<cstdio>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,x,y,a[N],b[N],ans;
inline bool check(CI st)
{
RI i; for (i=1;i<n&&st+i<=n;++i)
if (a[st+i]<b[i]) return 0; return 1;
}
int main()
{
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d",&n),i=1;i<n;++i)
scanf("%d%d%d",&x,&y,&b[i]);
for (i=1;i<=n;++i) scanf("%d",&a[i]);
sort(a+1,a+n+1); sort(b+1,b+n);
int l=0,r=n,mid; while (l<=r)
if (check(mid=l+r>>1)) ans=mid,r=mid-1; else l=mid+1;
printf("%d
",ans);
}
return 0;
}
Test Generation
瞎JB乱搞题,大力无脑随机喜提73pts
PS:题目中说了不能有重边但checker没有判233
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
const int N=76,M=125,S=35;
using namespace std;
typedef pair <int,int> pi;
int t; vector <pi> E;
inline int Rand(CI x)
{
return 1LL*rand()*rand()%x+1;
}
inline void link(CI x,CI y)
{
E.push_back(make_pair(x,y));
}
inline void Output(void)
{
random_shuffle(E.begin(),E.end());
for (vector <pi>:: iterator it=E.begin();it!=E.end();++it)
printf("%d %d
",it->first,it->second); E.clear();
}
int main()
{
srand(20030909); RI i; printf("%d %d
",N+4,M+8);
for (i=2;i<=N;++i) link(Rand(i-1),i);
for (i=1;i<=S;++i) link(1,Rand(N));
for (i=N;i<=M-S;++i) link(Rand(N),Rand(N));
for (i=1;i<=4;i+=2) link(i+N,N),link(i+N,i+N+1),link(i+N,N),link(i+N,i+N+1);
for (Output(),i=1;i<=N-1;++i) link(i,N-Rand(N-i)+1);
for (i=1;i<=S;++i) link(N,Rand(N));
for (i=N;i<=M-S;++i) link(Rand(N),Rand(N));
for (i=1;i<=4;i+=2) link(i+N,N),link(i+N,N),link(i+N,i+N+1),link(i+N,i+N+1);
return Output(),0;
}
Scoring Pairs
首先容易想到分开来算贡献,考虑两个数计算贡献一定是排序后按位来减一减,因此如果我们能求出一个数组(w_{i,j})表示从小到大排序后第(i)位的数字(j)出现的次数,就可以直接算答案
考虑求这个个数,是个人都知道用数位DP做
考虑我们先枚举一位(p),假定这一位没卡到上界,即后面的位都可以随便选
然后枚举这位的数字(s),再枚举一个(i)表示算数字(i)的贡献
计算过程比较巧妙,枚举一个(j)表示(i)这个数的个数,然后我们发现只要再枚举一个(k)表示小于(i)的数个数那么数字(i)所在的大小区间就被定下来了,而此时显然方案数也很好计算
然后就做完了,复杂度看起来很大,但众所周知常数小上界跑不到,因此跑的飞快
#include<cstdio>
#include<algorithm>
#define RI int
#define CI const int&
using namespace std;
typedef long long LL;
const int mod=1e9+7,N=20;
int t,num[N],ct[N],len,C[N][N],pw[N][N],w[N][N],ans; LL l,r;
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
inline void dec(int& x,CI y)
{
if ((x-=y)<0) x+=mod;
}
inline int sum(CI x,CI y)
{
int t=x+y; return t>=mod?t-mod:t;
}
inline void init(CI n)
{
RI i,j; for (C[0][0]=i=1;i<=n;++i) for (C[i][0]=j=1;j<=i;++j)
C[i][j]=sum(C[i-1][j],C[i-1][j-1]); pw[0][0]=1;
for (i=1;i<=n;++i) for (pw[i][0]=j=1;j<=n;++j) pw[i][j]=1LL*pw[i][j-1]*i%mod;
}
inline void resolve(LL x)
{
for (RI i=0;i<20;++i) num[i]=0; len=0; while (num[++len]=x%10,x/=10);
}
inline void calc(const LL& x,CI opt)
{
RI p,i,j,k,s,r; for (i=0;i<10;++i) ct[i]=0;
if (!~opt) { int tp=len; resolve(x); len=tp; } else resolve(x);
for (p=len;p;--p) //the unlimited position
{
for (s=0;s<num[p]+(p==1);++s) //which is put to p
{
for (i=s;i<10;++i) ++ct[i];
for (i=0;i<10;++i) //calc i's contribution
for (j=0;j<p;++j) //how many is i
for (k=0;k<p-j;++k) //how many more is less than i
{
int dlt=1LL*C[p-1][j]*C[p-1-j][k]%mod*pw[i][k]%mod*pw[9-i][p-1-j-k]%mod;
for (r=(i?ct[i-1]:0)+k+1;r<=ct[i]+k+j;++r)
if (~opt) inc(w[r][i],dlt); else dec(w[r][i],dlt);
}
for (i=s;i<10;++i) --ct[i];
}
for (i=num[p];i<10;++i) ++ct[i];
}
}
int main()
{
for (scanf("%d",&t),init(19);t;--t)
{
scanf("%lld%lld",&l,&r); calc(r,1); calc(l-1,-1);
RI i,j,k; for (i=1;i<=19;++i) for (j=0;j<10;++j) w[i][j]%=mod;
for (ans=0,i=1;i<=19;++i) for (j=0;j<10;++j) for (k=0;k<10;++k)
inc(ans,1LL*abs(j-k)*w[i][j]%mod*w[i][k]%mod);
for (printf("%d
",ans),i=1;i<=19;++i) for (j=0;j<10;++j) w[i][j]=0;
}
return 0;
}
Binomial Fever
压轴题巨TM简单。考虑:
如果我们可以把((p^i)^{underline{r}})展开得到每一项的系数,那么显然我们可以直接对同幂次的进行等比数列求和
翻一翻组合数学就知道:
然后第一类斯特林数的(O(nlog n))做法看这里:Luogu P5408 【模板】第一类斯特林数·行
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=1e6+5,mod=998244353;
int t,n,p,r,lim,ans,F[N<<1],fact[N],inv[N];
inline int sum(CI x,CI y)
{
int t=x+y; return t>=mod?t-mod:t;
}
inline int sub(CI x,CI y)
{
int t=x-y; return t<0?t+mod:t;
}
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
}
namespace Poly
{
int rev[N<<1],p;
inline void init(CI n)
{
for (lim=1,p=0;lim<=n;lim<<=1,++p);
for (RI i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<p-1);
}
inline void NTT(int *f,CI opt)
{
RI i,j,k; for (i=0;i<lim;++i) if (i<rev[i]) swap(f[i],f[rev[i]]);
for (i=1;i<lim;i<<=1)
{
int m=i<<1,D=quick_pow(3,~opt?(mod-1)/m:mod-1-(mod-1)/m);
for (j=0;j<lim;j+=m)
{
int W=1; for (k=0;k<i;++k,W=1LL*W*D%mod)
{
int x=f[j+k],y=1LL*f[i+j+k]*W%mod;
f[j+k]=sum(x,y); f[i+j+k]=sub(x,y);
}
}
}
if (!~opt)
{
int Inv=quick_pow(lim); for (i=0;i<lim;++i) f[i]=1LL*f[i]*Inv%mod;
}
}
inline void convolution(int *f,CI n,CI c,int *g)
{
static int A[N<<1],B[N<<1]; RI i; int bs; init(n<<1);
for (i=0;i<n;++i) A[n-1-i]=1LL*f[i]*fact[i]%mod;
for (bs=1,i=0;i<n;++i,bs=1LL*bs*c%mod) B[i]=1LL*bs*inv[i]%mod;
for (i=n;i<lim;++i) A[i]=B[i]=0; NTT(A,1); NTT(B,1);
for (i=0;i<lim;++i) A[i]=1LL*A[i]*B[i]%mod; NTT(A,-1);
for (i=0;i<n;++i) g[i]=1LL*A[n-1-i]*inv[i]%mod;
}
inline void solve(CI n,int *f)
{
if (!n) return (void)(f[0]=1); static int A[N<<1],B[N<<1];
RI i; int m=n>>1; solve(m,f); convolution(f,m+1,m,A);
for (i=0;i<=m;++i) B[i]=f[i]; for (i=m+1;i<lim;++i) A[i]=B[i]=0;
for (init(n),NTT(A,1),NTT(B,1),i=0;i<lim;++i) A[i]=1LL*A[i]*B[i]%mod;
NTT(A,-1); if (!(n&1)) for (i=0;i<=n;++i) f[i]=A[i]; else
for (i=0;i<=n;++i) f[i]=sum(i?A[i-1]:0,1LL*(n-1)*A[i]%mod);
//for (printf("%d
",n),i=0;i<=n;++i) printf("%d%c",f[i],"
"[i==n]);
}
};
inline int calc(CI q,CI n)
{
if (q==1) return sum(1,n);
return 1LL*sub(1,quick_pow(q,n+1))*quick_pow(sub(1,q))%mod;
}
int main()
{
for (scanf("%d",&t),init(1e6);t;--t)
{
RI i; for (scanf("%d%d%d",&n,&p,&r),Poly::solve(r,F),ans=i=0;i<=r;++i)
inc(ans,1LL*((r-i)&1?mod-F[i]:F[i])*calc(quick_pow(p,i),n)%mod);
for (printf("%d
",1LL*ans*inv[r]%mod),i=0;i<=r;++i) F[i]=0;
}
return 0;
}
(Challenge) Cubical Virus
这场刷排名最关键的一题,随手贪心搞了35pts
考虑单独看某个(1 imes n imes n)的正方形,一共有(n)个,显然我们贪心地把空格子多的放在前面
然后排序的时候需要两个参数(玄学),第一关键字是感染后的空格子数目,第二关键字是感染前的空格子数目
三个维度排三次即可
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define RI register int
#define CI const int&
#define Ms(A,B) memcpy(A,B,sizeof(A))
using namespace std;
const int N=105;
struct data
{
int val,rtg,id;
friend inline bool operator <(const data& A,const data& B)
{
return A.val!=B.val?A.val<B.val:A.rtg<B.rtg;
}
}v[N]; int n,p[N],q[N],r[N]; bool s[N][N][N],t[N][N][N],h[N][N][N];
inline char get_digit(void)
{
char ch; while (!isdigit(ch=getchar())); return ch;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j,k; for (scanf("%d",&n),k=1;k<=n;++k)
for (j=1;j<=n;++j) for (i=1;i<=n;++i) s[i][j][k]=get_digit()-48;
for (Ms(h,s),i=1;i<=n;++i) for (j=1;j<=n;++j)
for (k=1;k<=n;++k) if (h[i][j][k]) h[i][j+1][k]=h[i][j][k+1]=1;
for (i=1;i<=n;++i) v[i].val=v[i].rtg=0;
for (i=1;i<=n;++i) for (v[i].id=i,j=1;j<=n;++j)
for (k=1;k<=n;++k) v[i].val+=h[i][j][k];
for (i=1;i<=n;++i) for (v[i].id=i,j=1;j<=n;++j)
for (k=1;k<=n;++k) v[i].rtg+=s[i][j][k];
for (sort(v+1,v+n+1),i=1;i<=n;++i) p[i]=v[i].id;
for (i=1;i<=n;++i) for (j=1;j<=n;++j) for (k=1;k<=n;++k)
t[i][j][k]=s[p[i]][j][k]; Ms(s,t);
for (Ms(h,s),j=1;j<=n;++j) for (i=1;i<=n;++i)
for (k=1;k<=n;++k) if (h[i][j][k]) h[i+1][j][k]=h[i][j][k+1]=1;
for (j=1;j<=n;++j) v[j].val=v[j].rtg=0;
for (j=1;j<=n;++j) for (v[j].id=j,i=1;i<=n;++i)
for (k=1;k<=n;++k) v[j].val+=h[i][j][k];
for (j=1;j<=n;++j) for (v[j].id=j,i=1;i<=n;++i)
for (k=1;k<=n;++k) v[j].rtg+=s[i][j][k];
for (sort(v+1,v+n+1),j=1;j<=n;++j) q[j]=v[j].id;
for (i=1;i<=n;++i) for (j=1;j<=n;++j) for (k=1;k<=n;++k)
t[i][j][k]=s[i][q[j]][k]; Ms(s,t);
for (Ms(h,s),k=1;k<=n;++k) for (i=1;i<=n;++i)
for (j=1;j<=n;++j) if (h[i][j][k]) h[i+1][j][k]=h[i][j+1][k]=1;
for (k=1;k<=n;++k) v[k].val=v[k].rtg=0;
for (k=1;k<=n;++k) for (v[k].id=k,i=1;i<=n;++i)
for (j=1;j<=n;++j) v[k].val+=h[i][j][k];
for (k=1;k<=n;++k) for (v[k].id=k,i=1;i<=n;++i)
for (j=1;j<=n;++j) v[k].rtg+=s[i][j][k];
for (sort(v+1,v+n+1),k=1;k<=n;++k) r[k]=v[k].id;
for (i=1;i<=n;++i) for (j=1;j<=n;++j) for (k=1;k<=n;++k)
t[i][j][k]=s[i][j][r[k]]; Ms(s,t);
for (i=1;i<=n;++i) printf("%d%c",p[i],"
"[i==n]);
for (j=1;j<=n;++j) printf("%d%c",q[j],"
"[j==n]);
for (k=1;k<=n;++k) printf("%d%c",r[k],"
"[k==n]);
return 0;
}