T1 请问这还是纸牌游戏吗 https://scut.online/p/567

这道题正解据说是方根 这里先放着等以后填坑吧qwq

但是由于这道题数据是随机的 所以其实是有各种水法的(但是我比赛根本没有想到任何水法qwq

第一种水法呢 因为数据随机 所以当数据大小变得比较大的时候 基本乘出来的数已经能覆盖1到P-1了 所以我们直接输出1和P-1就可以了

而数据比较小的时候就可以暴力计算了qwq

include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
const int M=2e6+;
int read(){
int ans=,f=,c=getchar();
while(c<''||c>''){if(c=='-') f=-; c=getchar();}
while(c>=''&&c<=''){ans=ans*+(c-''); c=getchar();}
return ans*f;
}
LL mod,n,m;
int v1[M],v2[M],cnt1,cnt2;
LL h1[M],h2[M];
int main(){
LL x;
mod=read(); n=read(); m=read();
for(int i=;i<=n;i++){
x=read();
if(!v1[x]) v1[x]=,h1[++cnt1]=x;
}
for(int i=;i<=m;i++){
x=read();
if(!v2[x]) v2[x]=,h2[++cnt2]=x;
}
if(cnt1*cnt2>(5e7)){printf("1 %d\n",mod-); return ;}
LL ans1=mod-,ans2=;
for(int i=;i<=cnt1;i++)
for(int j=;j<=cnt2;j++){
ans1=min(ans1,h1[i]*h2[j]%mod);
ans2=max(ans2,h1[i]*h2[j]%mod);
}
printf("%lld %lld\n",ans1,ans2);
return ;
}

第二种写法呢 我们就可以从枚举P-1开始枚举答案最大是多少 然后枚举每一个a i 看是否存在b i 使得ab%P==枚举的答案

这里利用费马小定理预处理好了每一个a i对应的逆元fv i 所以可以根据枚举的答案和a i直接计算出b所对应的值

然后判断这个值是否存在就可以啦

这样子写的话想要卡掉其实是非常困难的 因为如果答案在中间部分(需要枚举大概n/2次答案()

那么一定会存在很多重复的数 而我们一开始就可以先把重复数去掉(也就是去重)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
const int M=2e6+;
int read(){
int ans=,f=,c=getchar();
while(c<''||c>''){if(c=='-') f=-; c=getchar();}
while(c>=''&&c<=''){ans=ans*+(c-''); c=getchar();}
return ans*f;
}
LL mod,n,m;
int v1[M],v2[M],cnt1,cnt2;
LL h1[M],h2[M],fv[M];
LL qmod(LL a,LL b){
LL ans=;
while(b){
if(b&) ans=ans*a%mod;
b>>=;
a=a*a%mod;
}
return ans;
}
int main(){
LL x,ans1,ans2,f;
mod=read(); n=read(); m=read();
for(int i=;i<=n;i++){
x=read();
if(!v1[x]){
v1[x]=;
h1[++cnt1]=x;
fv[i]=qmod(x,mod-);
}
}
for(int i=;i<=m;i++){
x=read();
if(!v2[x]) v2[x]=,h2[++cnt2]=x;
}
for(f=,ans1=;;ans1++){
for(int i=;i<=cnt1;i++)
if(v2[ans1*fv[i]%mod]){f=; break;}
if(f) break;
}
for(f=,ans2=mod-;;ans2--){
for(int i=;i<=cnt1;i++)
if(v2[ans2*fv[i]%mod]){f=; break;}
if(f) break;
}
printf("%lld %lld\n",ans1,ans2);
return ;
}

T2 请问你喜欢跑步吗 https://scut.online/p/568

这道题就很明显是道水题了 找出每秒所有面包店中价值最大的面包 然后求和就可以了

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#define LL long long
#define lowbit(x) x&-x
using namespace std;
const int M=;
int read(){
int ans=,f=,c=getchar();
while(c<''||c>''){if(c=='-') f=-; c=getchar();}
while(c>=''&&c<=''){ans=ans*+(c-''); c=getchar();}
return ans*f;
}
int n,m;
LL ans,s[M][M],k[M][M];
int main(){
n=read(); m=read();
for(int i=;i<=n;i++) for(int j=;j<=m;j++) s[i][j]=read();
for(int i=;i<=m;i++){
LL sum=;
for(int x=;x<=n;x++) sum=max(sum,s[x][i]);
ans+=sum;
}
printf("%lld\n",ans);
return ;
}

T3 请问穿着熊厉害吗 https://scut.online/p/569

这道题枚举到 √n就可以了

n到n-1 会对应大于 √n的另一段 具体的话通过手动模拟应该能有所体会 细节还是蛮多的

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<set>
#define LL long long
using namespace std;
LL read(){
LL ans=,f=,c=getchar();
while(c<''||c>''){if(c=='-') f=-; c=getchar();}
while(c>=''&&c<=''){ans=ans*+(c-''); c=getchar();}
return ans*f;
}
LL T,n,q,ans,last,G,x;
int main(){
T=read();
while(T--){
n=read();
if(n==){puts(""); continue;}
q=*n; ans=q+n; last=n;
//printf("%lld\n",ans);
for(x=;x*x<=q;x++){
ans+=q/x;
if(q%x) G=q/x;
else G=q/x;
ans=ans+(x-)*(last-G);
//printf("%d %d\n",last,G);
last=G;
}
ans+=(last-x+)*(x-);
printf("%lld\n",ans);
//printf("%lld %lld\n",last,x);
}
return ;
}

T4 请问机关锁打得开吗 https://scut.online/p/570

这道题9个锁 每个锁4种状态 一共也就2^18种状态 利用二进制来表示每一种状态

比如第一位和第二位表示第一个锁的状态也就是

00表示状态1

01表示状态2

10表示状态3

11表示状态四

以此类推 然后用bfs扫一遍就可以得到答案了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
const int Q=,P=(<<)+;
int read(){
int ans=,f=,c=getchar();
while(c<''||c>''){if(c=='-') f=-; c=getchar();}
while(c>=''&&c<=''){ans=ans*+(c-''); c=getchar();}
return ans*f;
}
int S,p[Q],s[Q][Q];
int G(int k,int x){
int w=;
if(k&(<<(*x))) w+=;
if(k&(<<(*x-))) w+=;
return w;
}
void nsp(int &k,int x,int w){
if(k&(<<(*x))) k-=(<<(*x));
if(k&(<<(*x-))) k-=(<<(*x-));
if(w&) k+=(<<(*x-));
if(w&(<<)) k+=(<<(*x));
}
int vis[P],d[P],ans=-;
queue<int>q;
void bfs(){
q.push(S); vis[S]=; d[S]=;
while(!q.empty()){
int MM=q.front(); q.pop();
if(!MM){ans=d[MM]; break;}
for(int x=;x<=;x++){
int M=MM,w=G(M,x),T=s[x][w],l=G(M,T);
w=(w+)%; l=(l+)%;
nsp(M,x,w); nsp(M,T,l);
if(!vis[M]) vis[M]=,d[M]=d[MM]+,q.push(M);
}
}
}
int main(){
for(int i=;i<=;i++){
p[i]=read()-;
for(int j=;j<=;j++) s[i][j]=read();
}
for(int i=;i<=;i++) nsp(S,i,p[i]);
bfs(); printf("%d\n",ans);
return ;
}

T5 请问直方图里能画矩形吗 https://scut.online/p/571

这道题的话 对每一个位置 找出他向左向右所能延申的最远距离就好了

可以用单调栈维护

考虑向左的延申的情况 我们考虑从1位置扫到i(当前)位置

如果一个位置j 他的高度h【j】比你大 并且他位置在你之前(j<I)那么他就可以被忽略

因为要从右边经过你的话 最大高度也就只能是h【i】

向右同理

通过这样的方法我们就可以维护出每一个点向左向右所能延申的最远距离了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define LL long long
using namespace std;
const int M=1e6+;
int read(){
int ans=,f=,c=getchar();
while(c<''||c>''){if(c=='-') f=-; c=getchar();}
while(c>=''&&c<=''){ans=ans*+(c-''); c=getchar();}
return ans*f;
}
LL n,h[M],stk[M],top,l[M],r[M],ans;
int main(){
n=read();
stk[]=;
for(int i=;i<=n;i++) h[i]=read();
for(int i=;i<=n;i++){
while(top&&h[stk[top]]>=h[i]) top--;
l[i]=stk[top]+;
stk[++top]=i;
//for(int x=1;x<=top;x++) printf("%d ",h[stk[x]]); puts("");
}
top=; stk[]=n+;
for(int i=n;i>=;i--){
while(top&&h[stk[top]]>=h[i]) top--;
r[i]=stk[top]-;
stk[++top]=i;
}
//for(int i=1;i<=n;i++) printf("%d %d\n",l[i],r[i]);
for(int i=;i<=n;i++) ans=max(ans,(r[i]-l[i]+)*h[i]);
printf("%lld\n",ans);
return ;
}

当然我比赛的时候脑子一抽就用了set维护

就是将所有的点按从小到大排序 然后从最小开始枚举 将枚举到的点的位置丢到set的里面

这样到当前点时 所有高度比他小的点就已经被丢到set里面的 你就只需要找位置离你最近的点是谁就好了

这个用lower_bound实现的okay了 当然这样写复杂度要多一个log

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<set>
#define LL long long
using namespace std;
const int M=1e6+;
LL read(){
LL ans=,f=,c=getchar();
while(c<''||c>''){if(c=='-') f=-; c=getchar();}
while(c>=''&&c<=''){ans=ans*+(c-''); c=getchar();}
return ans*f;
}
LL n,r[M],ans,sum[M],l[M];
struct node{LL id,h;}e[M];
bool cmp(node a,node b){return a.h==b.h?a.id<b.id:a.h<b.h;}
set<LL>q;
set<LL>::iterator It;
int main(){
n=read();
for(int i=;i<=n;i++) e[i].h=read(),e[i].id=i;
sort(e+,e++n,cmp);
for(int i=;i<=n;i++){
if(q.empty()) r[e[i].id]=n+;
else{
It=q.end();
if(*--It<e[i].id) r[e[i].id]=n+;
else r[e[i].id]=*q.upper_bound(e[i].id);
}
//sum[e[i].id]=e[i].h*(r[e[i].id]-e[i].id);
q.insert(e[i].id);
}
q.clear();
for(int i=;i<=n;i++){
if(q.empty()) l[e[i].id]=;
else{
It=q.end();
if(*--It<(-e[i].id)) l[e[i].id]=;
else l[e[i].id]=*q.upper_bound(-e[i].id)*-;
}
//sum[e[i].id]=e[i].h*(r[e[i].id]-e[i].id);
q.insert(-e[i].id);
}
//for(int i=1;i<=n;i++) printf("%d %d\n",l[i],r[i]);
for(int i=;i<=n;i++) sum[e[i].id]=e[i].h*(r[e[i].id]-l[e[i].id]-);
for(int i=;i<=n;i++) ans=max(ans,sum[i]);
printf("%lld\n",ans);
return ;
}

T5  留坑待补

T6 留坑待补

T7  请问这是鸽子吗 https://scut.online/p/574

这道题的话 一共就26种字母 我们考虑将26种字母单独维护

对于每个字母 用一个树状数组维护前缀一共有多少个相同的xx(x为当前字母)对 当然 如aaa算两对

xx对以后一个位置作为代表参与计算

然后每次修改则涉及四次修改  删除两次 插入两次 看是否有xx对被破坏 或者是否有xx对形成

然后询问的时候26种各求一次就okay了 当然求和的时候注意细节 如原本为aaaaa的一段

求后三个位置(即aaa)的aa对数时 求出来应为3对 但是因为第一个a的前一位被切断了 所以需要减去1 即为2对

这个细节在求和时是需要注意的

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#define LL long long
#define lowbit(x) x&-x
using namespace std;
const int M=1e6+,N=2e5+;
int read(){
int ans=,f=,c=getchar();
while(c<''||c>''){if(c=='-') f=-; c=getchar();}
while(c>=''&&c<=''){ans=ans*+(c-''); c=getchar();}
return ans*f;
}
char c[M];
int m,n;
int s[][N];
void add(int w,int x,int v){
//printf("%d %d %d\n",w,x,v);
while(x<=n){
s[w][x]+=v;
x+=lowbit(x);
}
//for(int i=1;i<=n;i++) printf("%d ",s[w][i]); puts("");
}
int p_sum(int w,int x){
int ans=;
while(x) ans+=s[w][x],x-=lowbit(x);
return ans;
}
int main(){
scanf("%s",c+);
n=strlen(c+);
for(int i=;i<=n;i++) if(c[i]==c[i-]) add(c[i]-'a',i,);//puts("qwq");
//for(int i=1;i<=n;i++) printf("%d ",s[1][i]);
//printf("%d\n",p_sum(1,15));
int op,x,y,q;
m=read();
while(m--){
op=read(); x=read();
if(op==){
q=getchar();
if(c[x]==q) continue;
if(c[x]==c[x-]) add(c[x]-'a',x,-);
if(c[x]==c[x+]) add(c[x]-'a',x+,-);
c[x]=q;
if(c[x]==c[x-]) add(c[x]-'a',x,);
if(c[x]==c[x+]) add(c[x]-'a',x+,);
}
else{
y=read();
int ans=;
for(int i=;i<;i++){
int now=p_sum(i,y)-p_sum(i,x-);
//printf("%d %d\n",p_sum(i,y),p_sum(i,x-1));
if(now==&&c[x]==('a'+i)&&c[x]==c[x-]) now=;
if(now) ans+=;
}
printf("%d\n",ans);
}
}
return ;
}

T8 请问我可以用左手吗 https://scut.online/p/575

这道题就是单纯的高精度加法了

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#define LL long long
#define lowbit(x) x&-x
using namespace std;
int read(){
int ans=,f=,c=getchar();
while(c<''||c>''){if(c=='-') f=-; c=getchar();}
while(c>=''&&c<=''){ans=ans*+(c-''); c=getchar();}
return ans*f;
}
char a[],b[],c[],d[];
int la,lb,lc,ld,mx,now;
int main(){
scanf("%s",a+); la=strlen(a+);
scanf("%s",b+); lb=strlen(b+);
scanf("%s",c+); lc=strlen(c+);
mx=max(la,lb); mx=max(lc,mx);
while(mx){
int sum=now;
mx--;
if(la) sum+=(a[la]-''),la--;
if(lb) sum+=(b[lb]-''),lb--;
if(lc) sum+=(c[lc]-''),lc--;
now=sum/;
d[++ld]=sum%;
}
if(now) d[++ld]=now;
for(int i=ld;i>=;i--) printf("%d",d[i]);
return ;
}

T9 请问你强吗 https://scut.online/p/576

这道题就是单纯判断某些字母的出现次数

https://scut.online/p/576
05-18 05:57