比赛链接
A题
大意
略
思路
略
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn=2e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,l,r,s;
int a[maxn];
int vis[maxn];
signed main(){
int _;scanf("%d",&_);
while(_--){
scanf("%d",&n);
int cnt=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]==1||a[i]==3){
cnt++;
}
}
printf("%d
",cnt);
}
return 0;
}
B题
大意
要你构造数字(x)有(a)位,数字(y)有(b)位,(gcd(x,y))有(c)位
(1le a,ble9,1le cle min(a,b))
求数字(x,y)均不含有前导(0)
思路
构造方法有很多种
我是构造成(x=2^{k1} imes 10^{c-1}),(y=3^{k2} imes 10^{c-1})
显然这样符合题意
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn=2e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int a,b,c;
int cal(ll x){
int cnt=0;
while(x){
cnt++;
x=x/10;
}
return cnt;
}
signed main(){
int _;scanf("%d",&_);
while(_--){
scanf("%d%d%d",&a,&b,&c);
ll ans1=1,ans2=1;
c--;
while(c--){
ans1=ans1*10;
ans2=ans2*10;
}
while(cal(ans1)!=a){
ans1=ans1*2;
}
while(cal(ans2)!=b){
ans2=ans2*3;
}
printf("%lld %lld
",ans1,ans2);
}
return 0;
}
C题
大意
有一个长度为(n(2le nle 3e5))的数组(a(1le a[i]le50))
现在有(q(1le q le 3e5))次查询,每次查询一个数(x(1le xle50))
要你找到一个最小的位置(pos)且(a[pos]=x)
输出(pos)并且把这个值放到首位,其他位置进行相应的移动
思路
主要是要发现总共只有(50)类数,并且你会发现,每个答案其实都是每一类的位置最小值
而你把你移动到第一位,那么它的位置依然是这一类的最小值,所以只要维护(50)类的位置最小值即可
其他的不要考虑
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn=3e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,q;
int mi[100];
int ans[maxn];
signed main(){
scanf("%d%d",&n,&q);
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
if(mi[x]==0){
mi[x]=i;
}
}
for(int i=1,x;i<=q;i++){
scanf("%d",&x);
ans[i]=mi[x];
for(int j=1;j<=50;j++){
if(mi[j]<mi[x]){
mi[j]++;
}
}
mi[x]=1;
}
for(int i=1;i<=q;i++){
printf("%d ",ans[i]);
}
return 0;
}
D题
大意
要你构造一个长度为(n(nle2e5))的字符串,且只能使用前(k)位小写字母
要使得(s[i]=s[j]&&s[i+1]=s[j+1])的次数最小
任意输出一组解
思路
显然就是把相邻的两个字符当作一个整体
那么最多有(k^2)种组合
所以我只要构造前(k^2)的长度即可
要他们出现(k^2-1)种组合
且(s[k^2+1]=s[1])
让(s[k^2]s[k^2+1])这两个字符为最后一组合
那么让第一个元素为(a)
然后遍历的时候从第(k)个字母,从大到小遍历到最小的字母(a)
这样恰好使得(aa)没有出现过
我也不知道怎么证明其一定可以构造成功
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn=2e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,k;
char s[maxn];
bool vis[300][300];
signed main(){
scanf("%d%d",&n,&k);
s[1]='a';
for(int i=2;i<=k*k;i++){
for(int j=k;j>=1;j--){
if(!vis[s[i-1]-'a'+1][j]){
vis[s[i-1]-'a'+1][j]=1;
s[i]=j+'a'-1;
break;
}
}
}
int len=k*k;
for(int i=1;i<=n;i++){
printf("%c",s[(i-1)%len+1]);
}
return 0;
}
E题
大意
给你一个(n imes m(n imes mle3e5))的矩阵
其中有黑点和白点
你可以对白点进行染色,使白点变为红点或蓝点
求在所有情况中你最多可以在这个图中放多少个多米诺骨牌的总和(mod 998244353)
多米诺骨牌放置的规则如下
- 每个多米诺骨牌覆盖两个相邻的单元格;
- 每个单元最多覆盖一个多米诺骨牌;
- 如果将多米诺骨牌水平放置(它覆盖一行中的两个相邻单元格),则它应仅覆盖红色单元格;
- 如果将多米诺骨牌垂直放置(它覆盖其中一列中的两个相邻单元格),则它应仅覆盖蓝色单元格。
思路
本质上是个概率论问题qwq
首先设有(cnt)个白点,假设期望为(e),那么答案就是(2^{cnt} imes e),所以你只要求出期望即可
假设现在这个图是固定的,你已知哪些点为蓝色,哪些点为红色
那么你可以使用贪心的策略来计算到底有多少个多米诺骨牌
假设((x,y))和((x,y+1))能够构成一个多米诺骨牌,那么代表这两个都被染成红色
且((x,y+1))左边的连续红色的点必定是偶数,如果为奇数,那么((x,y))将会和((x,y-1))进行配对
所以假设((x,y))若和((x,y-1))变成多米诺骨牌
-
若((x,y-1))前面没有白点,那么概率为(frac{1}{4}),相当于把这两个点直接变成红色
-
若((x,y-1))前面有一个白点,那么概率为(frac{1}{4}-frac{1}{8}),相当于这两个点为红色,减去三个点都是红色情况
-
若((x,y-1))前面有两个白点,那么概率为(frac{1}{4}-frac{1}{8}+frac{1}{16}),相当于这两个点为红色,减去最后三个点都是红色情况,然后加上四个点都是红色的情况
以此类推
然后变成蓝点也是同理的,建议可以看官方题解更加详细
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn=3e5+5,inf=0x3f3f3f3f,mod=998244353;
const double eps=1e-6;
const ll ni=499122177;
// ni为2的逆元
int n,m;
string s[maxn];
ll fac[maxn],finv[maxn];
ll base[maxn];
ll qpow(ll a,ll b){
ll ans=1,base=a;
while(b){
if(b%2){
ans=ans*base%mod;
}
base=base*base%mod;
b=b>>1;
}
return ans;
}
signed main(){
fac[0]=finv[0]=1;
for(int i=1;i<=3e5;i++){
fac[i]=fac[i-1]*2%mod;
// fac[i]=2^i
finv[i]=finv[i-1]*ni%mod;
// finv[i]=1/fac[i];
}
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>s[i];
}
base[0]=1;
base[1]=0;
base[2]=finv[2];
for(int i=3;i<=3e5;i++){
if(i%2==1){
base[i]=((base[i-1]-finv[i])%mod+mod)%mod;
}else{
base[i]=((base[i-1]+finv[i])%mod+mod)%mod;
}
}
ll ans=0,cnt=0;
// cnt计算有d多少个白点
// ans为期望
for(int i=0;i<n;i++){
int temp=0;
for(int j=0;j<m;j++){
if(s[i][j]=='o'){
cnt++;
temp++;
}else{
temp=0;
}
ans=(ans+base[temp])%mod;
}
}
for(int j=0;j<m;j++){
int temp=0;
for(int i=0;i<n;i++){
if(s[i][j]=='o'){
temp++;
}else{
temp=0;
}
ans=(ans+base[temp])%mod;
}
}
ans=ans*fac[cnt]%mod;
printf("%lld
",ans);
return 0;
}