A
找最长的线段然后向上取整即可。
#include<bits/stdc++.h>
#define f(i,a,b) for(int i=a;i<=b;i++)
using ll = long long;
using db = double;
ll rd(){ll x=0,w=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-'){w=-1;}c=getchar();}while('0'<=c&&c<='9'){x=x*10+(c-'0');c=getchar();}return x*w;}
using namespace std;
const int MX=500100;
const int mod=998244353;
mt19937 mrd(time(0));
namespace Wunsch{
ll n,k,m,cnt,ans;
string s;
ll a[MX];
void work(){
n=rd();
cin>>s;
ll mx=0;
ll len=0;
f(i,0,n-1){
if(s[i]=='#')len++;
else{
mx=max(mx,len);
len=0;
}
}
mx=max(mx,len);
cout<<((mx+1)>>1)<<endl;
return ;
}
bool main(){
int T=rd();
while(T--)work();
return 1;
}
}
bool amiya=Wunsch::main();
int main(){;}
B
因为只能向后转移,只需要从前往后扫一遍,看看能否构造成以1开始以n-1结束的长度为n-1的数列,并让最后一位数大于等于n,如果可以则有解,反之无解。
#include<bits/stdc++.h>
#define f(i,a,b) for(int i=a;i<=b;i++)
using ll = long long;
using db = double;
ll rd(){ll x=0,w=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-'){w=-1;}c=getchar();}while('0'<=c&&c<='9'){x=x*10+(c-'0');c=getchar();}return x*w;}
using namespace std;
const int MX=500100;
const int mod=998244353;
mt19937 mrd(time(0));
namespace Wunsch{
ll n,k,m,cnt,ans;
string s;
ll a[MX];
void work(){
n=rd();
ll tmp=0;
f(i,1,n){
a[i]=rd();
}
f(i,1,n){
tmp+=a[i]-i;
if(tmp<0){
puts("NO");
return ;
}
}
puts("YES");
return ;
}
bool main(){
int T=rd();
while(T--)work();
return 1;
}
}
bool amiya=Wunsch::main();
int main(){;}
C
每个点u都向u+x,u-x,u+y,u-y位置连边,我们发现在同一个连通块内的点都可以随意交换,所以只需要用宽搜给下标找到所有的连通块,如果位置上对应的值不在下标所在的连通块中就不可能换到正确的位置(因为是排列,正确的位置一定是值和下标相等的位置)。
#include<bits/stdc++.h>
#define f(i,a,b) for(int i=a;i<=b;i++)
using ll = long long;
using db = double;
ll rd(){ll x=0,w=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-'){w=-1;}c=getchar();}while('0'<=c&&c<='9'){x=x*10+(c-'0');c=getchar();}return x*w;}
using namespace std;
const int MX=500100;
const int mod=998244353;
mt19937 mrd(time(0));
namespace Wunsch{
ll n,k,m,cnt,ans;
string s;
ll a[MX];
ll b[MX];
queue<ll>q;
void work(){
n=rd();ll x=rd();ll y=rd();
f(i,1,n){
a[i]=rd();
b[i]=0;
}
cnt=1;
f(i,1,n){
if(b[i])continue;
q.push(i);
b[i]=cnt;
while(!q.empty()){
ll u=q.front();q.pop();
if(u+x>=1&&u+x<=n&&(!b[u+x])){b[u+x]=cnt;q.push(u+x);}
if(u-x>=1&&u-x<=n&&(!b[u-x])){b[u-x]=cnt;q.push(u-x);}
if(u+y>=1&&u+y<=n&&(!b[u+y])){b[u+y]=cnt;q.push(u+y);}
if(u-y>=1&&u-y<=n&&(!b[u-y])){b[u-y]=cnt;q.push(u-y);}
}
cnt++;
}
bool ok=1;
f(i,1,n){
if(b[i]!=b[a[i]]){
ok=0;
break;
}
}
ok?puts("YES"):puts("NO");
return ;
}
bool main(){
int T=rd();
while(T--)work();
return 1;
}
}
bool amiya=Wunsch::main();
int main(){;}
我猜有纯数学做法就去看了一眼题解,下面是笔记。
令,显然我们每一步步长一定是g的倍数,所以在模意义下余数始终相等。下面我们要证明余数相等的地方我们一定能到达。由裴蜀定理可得存在和使得ax + by = g成立,即对于任意的g的倍数都一定有解。到这一步我们就会发现,如果忽略数组边界的话一定存在方案使得同一个余数类是联通的。而题目保证了,此时,假设我们在位置:
- 想向左走 (即 ),但发现越界了,说明 。此时因为 ,一定有:,所以向右走 是安全的。
- 反过来,如果我们想向右走,但发现越界了,说明 。因为 ,所以,向左走是安全的。
所以,我们就得到了能排序的充要条件,即。所以只需要判断每个位置的值与下标是否在模的一个同余类中即可。
