题意:有一串01串,0代表男生,1代表女生,你可以进行添加男生操作,使得对于任意一段 [ l , r ] [l,r] [l,r](r>=l+1)中男生的数量不超过女生。问最少操作几次。
题解:首先对于男生来说,两边都必须为女生,对于长度为3的时候,女生数量必须大于等于2。也就是说两个男生之间必须要有至少两个女生。
void solve(){
int n;
cin>>n;
string s;
cin>>s;
int a=0,b=0;
vector<int>v;
for(int i=0;i<n;i++){
if(s[i]=='0')v.push_back(i);
}
int ans=0;
for(int i=1;i<v.size();i++){
ans+=max(0,2-(v[i]-v[i-1]-1));
}
cout<<ans<<"\n";
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int t;
cin>>t;
// t=1;
while(t--){
solve();
}
return 0;
}
题意: p 1 , p 2 , p 3 . . . p n p_1,p_2,p_3...p_n p1,p2,p3...pn为 [ 1 , n ] [1,n] [1,n]的排列,问满足 g c d ( 1 ∗ p 1 , 2 ∗ p 2 , . . . n ∗ p n ) > 1 gcd(1*p_1,2*p_2,...n*p_n)>1 gcd(1∗p1,2∗p2,...n∗pn)>1的方案数。
题解:
对于相邻的两个数,一奇一偶 g c d gcd gcd为 1 1 1,为了使得 g c d > 1 gcd>1 gcd>1,我们必须把奇数安排在偶数位置,因此当n为奇数时,有一个奇数没有偶数位置安排,答案为0,n为偶数时,奇偶数分开全排列, a n s = ( n 2 ! n 2 ! ) ans=(\frac{n}{2}!\frac{n}{2}!) ans=(2n!2n!)
ll fac[maxn];
void solve(){
ll n;
cin>>n;
if(n%2)cout<<"0\n";
else cout<<fac[n/2]*fac[n/2]%MOD<<"\n";
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int t;
cin>>t;
// t=1;
fac[0]=1;
for(ll i=1;i<maxn;i++)fac[i]=fac[i-1]*i%MOD;
while(t--){
solve();
}
return 0;
}
题意:现在有一个 [ 1 , n ] [1,n] [1,n]的数组P,但是顺序忘记了,只知道它的C数组,问这个C数组是否合法。
定义B数组为 b i = m a x ( p 1 , p 2 , . . . p i ) b_i=max(p_1,p_2,...p_i) bi=max(p1,p2,...pi)
定义C数组为 c i = p 循 环 右 移 ( i − 1 ) 位 得 到 的 b 数 组 去 重 后 的 个 数 c_i=p循环右移(i-1)位得到的b数组去重后的个数 ci=p循环右移(i−1)位得到的b数组去重后的个数
首先B数组一定是递增的。
当最大值 n n n在第一个时, B = n , n , . . n , c i = 1 B={n,n,..n},c_i=1 B=n,n,..n,ci=1。当c等于1时说明当前位置最大值在第一个位置。
那么我们对c数组移动使得1在第一个位置,这样当我们循环右移时最大值后面的状态都不用考虑,因为取max都为 n n n。当当前最后一个数移动到第一位时,然后小于后面的数,他就会使得c+1,所以相邻的两项最多只会差1,并且后面的c都不可能为1.
int a[maxn];
void solve(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
vector<int>v;
int index=-1;
for(int i=1;i<=n;i++){
if(a[i]==1){
index=i;
break;
}
}
if(index==-1){
cout<<"NO\n";
return;
}
for(int i=index;i<=n;i++)v.push_back(a[i]);
for(int i=1;i<index;i++)v.push_back(a[i]);
for(int i=1;i<n;i++){
if(v[i]>i+1||v[i]==1||v[i]>v[i-1]+1){
cout<<"NO\n";
return;
}
}
cout<<"YES\n";
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int t;
cin>>t;
// t=1;
while(t--){
solve();
}
return 0;
}
题意: [ l , r ] 区 间 中 的 所 有 数 异 或 上 x 得 到 a 数 组 [l,r]区间中的所有数异或上x得到a数组 [l,r]区间中的所有数异或上x得到a数组,问 x x x的值。 ( l = = 0 ) (l==0) (l==0)
题解:
对 [ l , r ] [l,r] [l,r]中的数进行二进制拆分,对 [ a 1 , a r − l + 1 ] [a_1,a_{r-l+1}] [a1,ar−l+1]也进行拆分,若拆分后第 i i i位的个数不相同,说明这一位进行了异或操作,使得个数翻转了,将这一位计入答案。
int num[30],num2[30];
void solve(){
int l,r;
cin>>l>>r;
for(int i=0;i<=17;i++)num[i]=0;
for(int i=0;i<=17;i++)num2[i]=0;
for(int i=1,x;i<=r-l+1;i++){
cin>>x;
for(int j=0;j<=17;j++){
if((x>>j)&1){
num[j]++;
}
}
}
for(int i=l;i<=r;i++){
for(int j=0;j<=17;j++){
if((i>>j)&1){
num2[j]++;
}
}
}
int ans=0;
for(int i=0;i<=17;i++){
if(num[i]==num2[i])continue;
else ans+=(1<<i);
}
cout<<ans<<"\n";
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int t;
cin>>t;
// t=1;
while(t--){
solve();
}
return 0;
}
题意:同D1,但是 ( l > = 0 ) (l>=0) (l>=0)
题解:当 l ! = 0 l!=0 l!=0时,上面的构造方法就存在问题。
首先我们得知道 [ l , r ] [l,r] [l,r]中得值,两两异或都不为0,因为任意两个值都不相等。那么所有值都异或上 x x x后,所有值也都不相等,因为异或后得两个值异或等价于 ( i ⨁ x ) ⨁ ( j ⨁ x ) = ( i ⨁ j ) (i \bigoplus x) \bigoplus (j \bigoplus x)=(i \bigoplus j) (i⨁x)⨁(j⨁x)=(i⨁j),所以异或后的任意两个值也不相等,那么我们枚举 x x x的时候,只需要计算a数组异或上 x x x的最小值为 l l l,最大值为 r r r就可以确定这个数组异或上 x x x得到的为 [ l , r ] [l,r] [l,r]
异或最大最小值用01字典树可以log的算出,枚举x的时间为n,总时间为nlog
int tol; //节点个数
ll val[32*maxn]; //点的值
int ch[32*maxn][2]; //边的值
void init()
{ //初始化
for(int i=0;i<=tol;i++)ch[i][0]=ch[i][1]=0;
tol=1;
}
void insert(ll x)
{ //往 01字典树中插入 x
int u=0;
for(int i=16;i>=0;i--)
{
int v=(x>>i)&1;
if(!ch[u][v])
{ //如果节点未被访问过
ch[tol][0]=ch[tol][1]=0; //将当前节点的边值初始化
val[tol]=0; //节点值为0,表示到此不是一个数
ch[u][v]=tol++; //边指向的节点编号
}
u=ch[u][v]; //下一节点
}
val[u]=x; //节点值为 x,即到此是一个数
}
ll query(ll x)
{ //查询所有数中和 x异或结果最大的数
int res=0;
int u=0;
for(int i=16;i>=0;i--)
{
int v=(x>>i)&1;
//利用贪心策略,优先寻找和当前位不同的数
if(ch[u][v^1]) u=ch[u][v^1],res|=(1<<i);
else u=ch[u][v];
}
return res ;//返回结果
}
ll query_min(ll x)
{ //查询所有数中和 x异或结果最小的数
int res=0;
int u=0;
for(int i=16;i>=0;i--)
{
int v=(x>>i)&1;
//利用贪心策略,优先寻找和当前位不同的数
if(ch[u][v]) u=ch[u][v];
else u=ch[u][v^1],res|=(1<<i);
}
return res;//返回结果
}
void solve(){
int l,r,x;
cin>>l>>r;
init();
for(int i=l;i<=r;i++){
cin>>x;
insert(x);
}
for(int i=l;i<=r;i++){
int ans=x^i;
int mx=query(ans);
int mn=query_min(ans);
if(mn==l&&mx==r){
cout<<ans<<"\n";
return;
}
}
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
题意:两个人博弈,有一个n*n的棋盘,每个点都有不同的值,两个下的棋子之间的曼哈顿距离不得小于等于k,问对于每个点,若先手下这个点,谁获胜。,每人下 1 0 100 次 10^{100}次 10100次
题解:对于后手下的人,若在先手的k距离外,没有比k距离内的值大,就输了,因为先手的一直都比你大,你又进不去k的范围内。反之后手就赢了。
按点值从大到小枚举,最大的点值先手下必胜,再进行转移,对于所有的必胜点都得在目前点的k范围内,只有这样目前点才能为必胜点。
判断是否覆盖所有必胜点只需要维护上下左右四个端点值。
int u,d,l,r,k,n;
bool check(int i,int j){
return (abs(i-u)<=k&&abs(i-d)<=k&&abs(j-l)<=k&&abs(j-r)<=k);
}
int dp[2222][2222];
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin>>n>>k;
vector<array<int,5>>v;
for(int i=1;i<=n;i++){
for(int j=1,x;j<=n;j++){
cin>>x;
v.push_back({x,i,j,i+j,j-i});
}
}
sort(v.begin(),v.end(),greater<array<int,5>>());
u=v[0][3];d=v[0][3];l=v[0][4];r=v[0][4];
for(auto &it:v){
if(check(it[3],it[4])){
dp[it[1]][it[2]]=1;
u=min(u,it[3]);
d=max(d,it[3]);
l=min(l,it[4]);
r=max(r,it[4]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(dp[i][j])cout<<"M";
else cout<<"G";
}
cout<<"\n";
}
return 0;
}
题意:有一串01串,该S串的值定义为 n u m 1 n \frac{num1}{n} nnum1,你需要找k个连续字串拼成长度为 m m m的串,使得它们的值相同。
题解:
首先T字符串的1的值设为 x x x, x m = n u m 1 n \frac{x}{m}=\frac{num1}{n} mx=nnum1
若 n u m 1 ∗ m % n ! = 0 num1*m\%n!=0 num1∗m%n!=0无解。
否则 x = n u m 1 ∗ m / n x=num1*m/n x=num1∗m/n转化为找k个字串使得子串的长度为 m m m,1的数量为 x x x
存在一个结论k一定<=2;分类讨论。
我们现在还假设这个字符串是圆形的。考虑这个圆形字符串中任何长度为m的子数组,包括环绕的子数组。如果我们能找到一个恰好有 x x x个 1 1 1的子数,那么我们需要的最小零件数是 1 1 1或 2 2 2,这取决于它是否环绕。而这两者都可以用滑动窗口/前缀和来检查。
事实证明,这样的子阵列总是存在的。如果 i ≤ n − m + 1 i≤n-m+1 i≤n−m+1,让 c i c_i ci为 s [ i . . . i + m − 1 ] s[i...i+m-1] s[i...i+m−1]中的 1 1 1的数量,否则为 s [ 1... m − ( n − i + 1 ) ] + s [ i . . . n ] s[1...m-(n-i+1)]+s[i...n] s[1...m−(n−i+1)]+s[i...n](即,环绕的子阵列与不环绕的子阵列)。请注意,对于所有 1 ≤ i < n 1≤i
假设 m a x ( c i ) ≥ x max(c_i)≥x max(ci)≥x和 m i n ( c i ) ≤ x min(c_i)≤x min(ci)≤x,意味着某些 c i = x c_i=x ci=x。假设 m a x ( c i ) < x max(c_i)
int num[maxn];
void solve(){
int n,m;
cin>>n>>m;
string s;
cin>>s;
num[0]=0;
for(int i=0;i<n;i++){
num[i+1]=num[i]+(s[i]=='1');
}
ll k=(ll)num[n]*m;
if(k%n){
cout<<"-1\n";
return;
}
k/=n;
int index=-1;
for(int i=1;i+m-1<=n;i++){
if(num[i+m-1]-num[i-1]==k){
index=i;
break;
}
}
if(index!=-1){
cout<<1<<"\n";
cout<<index<<" "<<index+m-1<<"\n";
return;
}
for(int i=1;i+(n-m)-1<=n;i++){
if(num[i+(n-m)-1]-num[i-1]==(num[n]-k)){
index=i;
break;
}
}
cout<<2<<"\n";
cout<<1<<" "<<index-1<<"\n";
cout<<index+(n-m)<<" "<<n<<"\n";
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}