第三届信大超越杯团体赛题解
A 红红找蓝蓝
题解:
宽搜bfs,定义状态{x,y,d,Dir}表示:到(x,y)点拐了d次弯,上一次的方向为Dir
与最短路不同的是,我们从一个点出发要把一个方向上的所有点加入队列,因为这个方向上所有点的拐弯数都只是+1,为了维护先搜到的点拐弯数越少,就要把一个方向上所有点加入队列。
注意不能省略vis[x][y],是为了剪除大量没有意义的搜索,并且如果不加的话会爆空间,别问我为啥知道
Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int n,ax,ay,bx,by;
bool canarrive;
char m[110][110],t;
bool vis[110][110];
int dir[4][2]={0,1,0,-1,1,0,-1,0};
void Read(){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
t=getchar();
while(t=='\n'||t==' ')t=getchar();
m[i][j]=t;
if(t=='A'){
ax=i;ay=j;
}
if(t=='B'){
bx=i;by=j;
}
}
}
}
struct node{
int x,y,d,Dir;//意义为:走到(x,y)拐了d次弯,上一次的方向是Dir
node(int xx,int yy,int dd,int ddd){
x=xx;y=yy;d=dd;Dir=ddd;
}
node(){}
};
bool check(int x,int y){
if(x<1||x>n||y<1||y>n||m[x][y]=='x')return false;
return true;
}
void bfs(){
memset(vis,0,sizeof vis);
queue<node> q;
node u;
int x,y,d;
q.push(node(ax,ay,-1,-1));
vis[ax][ay]=1;
while(!q.empty()){
u=q.front();q.pop();
x=u.x;y=u.y;d=u.d;
if(x==bx&&y==by){
printf("%d\n",d);
canarrive=1;
return;
}
for(int i=0;i<4;++i){
if(i==u.Dir)continue;
int vx=x+dir[i][0];
int vy=y+dir[i][1];
while(check(vx,vy)){
if(!vis[vx][vy]){
vis[vx][vy]=1;
q.push(node(vx,vy,d+1,i));
}
vx+=dir[i][0];vy+=dir[i][1];
}
}
}
printf("-1\n");
}
int main(){
int T=R();
while(T--){
n=R();
Read();
bfs();
}
return 0;
}
B 红红蓝蓝在争论
题解:
贪心+双指针扫描,我们把题目做两遍,把0变成1,以及把1变成0(下面只描述0变1)
很容易想到我们要变肯定是把相邻的0变成1,这样满足贪心策略
那我们把所有0的下标用vector存起来,l,r指针分别指向m个需要改变的0
那么就可以更新答案为 ans=max(ans,v[r+1]-v[l-1]-1)
l,r指针不断向右++
对于0的个数不足m个以及扫描边界再特殊处理一下就OK了
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int n,m,ans,s_len;
string s;
vector<int> v[2];
void init(){
ans=0;
v[0].clear();
v[1].clear();
}
void work(int k){
int v_num=v[k].size();
if(v_num<=m){
ans=s_len;
return;
}
int l=0,r;
for(int i=0;i<v_num;++i){
if(i+1<=m){
ans=max(ans,v[k][i+1]);
}else{
r=i-1;
break;
}
}
l++;r++;
while(r<=v_num-1){
if(r==v_num-1) ans=max(ans,s_len-v[k][l-1]);
else ans=max(ans,v[k][r+1]-v[k][l-1]-1);
l++;r++;
}
}
int main(){
int T=R();
while(T--){
init();
n=R();m=R();
cin>>s;
s_len=s.size();
for(int i=0;i<s_len;++i){
v[s[i]-'0'].push_back(i);
}
work(1);
work(0);
printf("%d\n",ans);
}
return 0;
}
C 合法的 IP 地址
题解:
纯模拟,注意细节判断
给几种不合法的情况:
1..1.1 (这种容易出错)
1.1.1.1.
1x.1.1.1
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
string s;
bool faultchar(char c){
if(c>='0'&&c<='9'||c=='.')return false;
return true;
}
bool work(){
int s_len=s.size();
char c;
int num_cnt=0;
int a[5];
memset(a,-1,sizeof a);
for(int i=0;i<s_len;++i){
if(faultchar(s[i]))return false;
if(s[i]>='0'&&s[i]<='9'){
if(a[num_cnt]==-1)a[num_cnt]=0;
a[num_cnt]=a[num_cnt]*10+s[i]-'0';
}else{
num_cnt++;
}
}
if(num_cnt!=3)return false;
if(a[0]<=0||a[0]>255)return false;
for(int i=1;i<4;++i){
if(a[i]<0||a[i]>255)return false;
}
return true;
}
int main(){
int T=R();
while(T--){
cin>>s;
if(work()){
printf("YES\n");
}else{
printf("NO\n");
}
}
return 0;
}
D 石头剪刀布
题解:
真·签到题
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int main(){
int T=R();
while(T--){
int n=R()%3;
switch(n){
case 0: printf("Scissors\n");break;
case 1: printf("Paper\n");break;
case 2: printf("Rock\n");break;
}
}
return 0;
}
E 填充游戏
题解:
比赛的时候把头都扣烂了没写出来,结果发现代码只有10
如果第一个⚪放不下,那么后手胜,如果第一个能放下,第一个放最中间,后面每次都放在与后手中心对称的位置,则先手必胜。
code:
#include<bits/stdc++.h>
using namespace std;
int main(){
int T=R();
while(T--){
int m=R(),n=R();
if(m<=2*n) puts("lanlan yyds");
else puts("honghong yyds");
}
return 0;
}
F RickLee And 540
题解:
总情况数:
至多选到 i 道的情况数:
由容斥定理:
刚好选到n道的情况数为:至多选n道-至多选n-1道+至多选n-2道......
所以:
注意:
- 取模一定要充分,可能有一个地方忘了取模就会全wa,如果乘法不放心的话就用__int128过渡一下。
- 阶乘的逆元可以在求阶乘的时候顺便求了,当然 inv(fac[i]) 理论上是没错的,因为全都是乘法和加法。
- 由于指数km较大,可以使用欧拉降幂公式:
当p为质数,
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=998244353;
ll fac[100010],invfac[100010];
ll ksm(ll a,ll b){
ll ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
ll inv(ll x){
return ksm(x,mod-2);
}
ll C(ll n,ll m){
return fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
void init(){
fac[0]=invfac[0]=1;
for(ll i=1;i<=100001;++i){
fac[i]=fac[i-1]*i%mod;
invfac[i]=invfac[i-1]*inv(i)%mod;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
init();
ll T;
cin>>T;
while(T--){
ll n,m,k;
cin>>n>>m>>k;
ll fuyi_n=1,ans=0,inv_n=inv(n);
for(ll i=n;i>=0;--i){
ans=(ans+fuyi_n*C(n,i)*ksm(i*inv_n%mod,k*m%(mod-1))%mod+mod)%mod; //用到欧拉降幂公式
fuyi_n*=-1;
}
cout<<ans%mod<<'\n';
}
return 0;
}
G Jump
题解:
线段树+递推,对于每个位置先求出到达它的最大经验值,再把这个值加到它所能到达的区间的线段树里去,然后对于后面的位置,单点求线段树最大值再加上自己的经验值即为 f[i] 。
线段树区间修改,单点询问。注意线段树空间要开4*n
code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
struct Tree{
ll l,r,d;
}o[200010*4];
ll n,m,ans,a[200010];
void build(ll i,ll L,ll R){
o[i].l=L;o[i].r=R;
o[i].d=-0x3f3f3f3f;//附加性质初始化
if(L==R)return;
ll mid=(L+R)>>1;
build(i<<1,L,mid);
build(i<<1|1,mid+1,R);
}
void Add2(ll i,ll L,ll R,ll d){
if(R<o[i].l||L>o[i].r)return;
if(L<=o[i].l&&o[i].r<=R){
o[i].d=max(o[i].d,d);return;
}
Add2(i<<1,L,R,d);
Add2(i<<1|1,L,R,d);
}
void Ask2(ll i,ll x){
if(x<o[i].l||x>o[i].r)return;
if(x>=o[i].l&&x<=o[i].r){
ans=max(ans,o[i].d);
}
if(o[i].l==o[i].r)return;
Ask2(i<<1,x);
Ask2(i<<1|1,x);
}
int main(){
n=R();
build(1,1,n);
for(ll i=1;i<=n;++i)a[i]=R();
ll l,r;
for(ll i=1;i<=n-1;++i){
l=R();r=R();
ans=-0x3f3f3f3f;
Add2(1,l,r,a[i]);
Ask2(1,i+1);
a[i+1]=ans+a[i+1];
}
printf("%lld",a[n]);
return 0;
}
H 信大学子要过河
题解:
贪心,先将a[n]从小到大排序,考虑对于当前时刻想要将最大的两个数渡河,有两种最优策略:
- 用最小数分别将两个最大数送过去 cost:a[1]*2+a[i]+a[i-1]
- 先让最小和次小数过河,让最小回来,再让两个最大数一起过河,让次小回来:
cost:a[1]+a[2]*2+a[i]
每次比较一下取最小值,在注意一下特殊情况和边界处理就ok了
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int R(){int a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int n,a[100010],ans;
void init(){
ans=0;
}
void work(){
if(n==1){
ans=a[1];
return;
}
for(int i=n;i-1>2;i-=2){
if(a[1]*2+a[i]+a[i-1]<a[1]+a[2]*2+a[i]){
ans+=a[1]*2+a[i]+a[i-1];
}else{
ans+=a[1]+a[2]*2+a[i];
}
}
if(n&1){ // 奇数
ans+=a[1]+a[2]+a[3];
}else{
ans+=a[2];
}
}
int main(){
int T=R();
while(T--){
init();
n=R();
for(int i=1;i<=n;++i){
a[i]=R();
}
sort(a+1,a+1+n);
work();
printf("%d\n",ans);
}
return 0;
}
I 中国国粹
题解:
模拟的神!好家伙,我下来调试了4个小时终于A了,我要哭了。。。有个小细节当时改的时候没考虑周全,调试了好久才发现这个问题。
感觉整个人都通透了
教训:仔细看题!!写代码细心!细心!再细心!
顺子型搭子不能考虑字牌!!!
屁胡的判断用枚举对子然后深搜4个搭子
不过好好把这道题写过,你的编程能力会有非常大的提高
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int R(){int a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int a[20],b[20],pos,val_count[50],ans[200],ans_cnt,val_a_cnt[50];
void init(){
memset(val_a_cnt,0,sizeof val_a_cnt);
memset(ans,0,sizeof ans);
ans_cnt=0;
memset(a,0,sizeof a);
memset(b,0,sizeof b);
}
bool pd1(){
for(int i=1;i<14;i+=2){
if(b[i]!=b[i+1])return false;
}
return true;
}
bool pd2(){
int num131[]={0,11,19,21,29,31,39,41,42,43,44,45,46,47},cnt131=13;
int vis[50]={0};
for(int i=1;i<=cnt131;++i){
vis[num131[i]]=1;
}
for(int i=1;i<=14;++i){
if(!vis[b[i]])return false;
vis[b[i]]++;
}
int dui=0;
for(int i=1;i<=cnt131;++i){
int x=num131[i];
if(vis[x]==1)return false; //有的数没有
if(vis[x]==3){ //有对数
dui++;
}
}
if(dui>1)return false;
return true;
}
bool pd3(){
int jiang[]={0,12,15,18,22,25,28,32,35,38},jiang_num=9;
bool vis[50]={0};
for(int i=1;i<=jiang_num;++i){
vis[jiang[i]]=1;
}
for(int i=1;i<=14;++i){
if(!vis[b[i]])return false;
}
return true;
}
bool dfs(int s){
if(s==4){
for(int i=1;i<=4;++i){
for(int j=1;j<=(i==4?7:9);++j){
int x=i*10+j;
if(val_count[x])return false;
}
}
return true;
}
for(int i=1;i<=4;++i){
for(int j=1;j<=(i==4?7:9);++j){
int x=i*10+j;//枚举
if(!val_count[x])continue;
if(val_count[x]>=3){
val_count[x]-=3;
if(dfs(s+1))return true;
val_count[x]+=3;
}
if(i!=4&&j<=7&&val_count[x+1]&&val_count[x+2]){
val_count[x]--;
val_count[x+1]--;
val_count[x+2]--;
if(dfs(s+1))return true;
val_count[x]++;
val_count[x+1]++;
val_count[x+2]++;
}
}
}
return false;
}
bool pd4(){
for(int i=1;i<=4;++i){
for(int j=1;j<=(i==4?7:9);++j){
int x=i*10+j;//枚举
if(val_count[x]>=2){
val_count[x]-=2;
if(dfs(0))return true;
val_count[x]+=2;
}
}
}
return false;
}
void print(int x){
char c;
switch(x/10){
case 1:c='m';break;
case 2:c='s';break;
case 3:c='p';break;
case 4:c='c';
}
printf("%d%c ",x%10,c);
}
void work(){
for(int i=1;i<=13;++i)val_a_cnt[a[i]]++;
for(int i=1;i<=4;++i){
for(int j=1;j<=(i==4?7:9);++j){
int x=i*10+j;
if(val_a_cnt[x]==4)continue;
bool bj=1;
pos=0;
for(int k=1;k<=13;++k){ //插入排序
if(bj&&x<a[k]){
b[++pos]=x;
bj=0;
}
b[++pos]=a[k];
}
if(bj)b[++pos]=x;
memset(val_count,0,sizeof val_count);
for(int i=1;i<=14;++i)val_count[b[i]]++;
if(pd1()||pd2()||pd3()||pd4()){
ans[++ans_cnt]=x;
}
}
}
}
void out(){
if(ans_cnt!=0){
printf("%d ",ans_cnt);
sort(ans+1,ans+ans_cnt+1);
for(int i=1;i<=ans_cnt;++i){
print(ans[i]);
}
printf("\n");
}else{
printf("Ohno\n");
}
}
int main(){
int T=R();
while(T--){
init();
int number;char c;
for(int i=1;i<=13;++i){
scanf("%d%c",&number,&c);
switch(c){
case 'm':a[i]=10+number;break;
case 's':a[i]=20+number;break;
case 'p':a[i]=30+number;break;
case 'c':a[i]=40+number;
}
}
sort(a+1,a+14);
work();
out();
}
return 0;
}
K 最小整数问题
题解:
真·水题
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
string s;
int main(){
int T=R();
while(T--){
cin>>s;
sort(s.begin(),s.end());
int x=0;
while(s[x]=='0')x++;
swap(s[0],s[x]);
cout<<s<<'\n';
}
return 0;
}
J Kabayashi And Three consciousness
题解:
模拟+gcd
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
ll p,q;
ll cnt,ans[1000100];
ll gcd(ll a,ll b){
return (!b?a:gcd(b,a%b));
}
int main(){
p=R();q=R();
ll g=gcd(p,q);
p/=g;q/=g;
swap(p,q);
do{
swap(p,q);
ll zhengshu=p/q;
ans[++cnt]=zhengshu;
p-=q*zhengshu;
g=gcd(p,q);
p/=g;q/=g;
}while(p!=1);
ans[++cnt]=q;
printf("%lld\n",cnt);
for(ll i=1;i<=cnt;++i){
printf("%lld ",ans[i]);
}
return 0;
}
L Find And Cal
题解:
考大素数判断,由于素数的密度较大,可以直接从x++来找素数,用Miller Rabin判断是否为素数。
那么如何求 包含质因数y的个数?
f(x,y)=f(x/y,y)+x/y
推导:
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
const ll Mod=998244353;
ll prime[10]={2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
ll ksm(ll a,ll b,ll mod){
ll ans=1;
while(b){
if(b&1) ans=(__int128)ans*a%mod;
a=(__int128)a*a%mod;
b>>=1;
}
return ans;
}
bool miller_rabin(ll x){ //判断素数
ll s=0,t=x-1;
if(x==2)return true; //2是素数
if(x<2||!(x&1)) return false; //如果x是偶数或者是0,1,那它不是素数
while(!(t&1)){ //将x分解成(2^s)*t的样子
s++;
t>>=1;
}
for(ll i=0;i<10&&prime[i]<x;++i){ //随便选一个素数进行测试
ll a=prime[i];
ll b=ksm(a,t,x); //先算出a^t
for(int j=1;j<=s;++j){ //然后进行s次平方
ll k=ksm(b,2,x); //求b的平方
if(k==1&&b!=1&&b!=x-1)return false; //用二次探测判断
b=k;
}
if(b!=1)return false; //用费马小定律判断
}
return true; //如果进行多次测试都是对的,那么x就极有可能是素数
}
int main(){
ll T=R();
while(T--){
ll x=R(),y=R();
x++;
while(!miller_rabin(x))x++;
ll ans=0;
while(x){
ans=(ans+x/y)%Mod;
x/=y;
}
printf("%lld\n",ans);
}
return 0;
}
标签:return,信大,团体赛,int,题解,ll,long,while,getchar
From: https://www.cnblogs.com/l1fan/p/18098127/the-3rd-xinda-surpassed-the-cup-group-competition