蓝书题单
刷题签到记录,长期更新。部分会借鉴于别人代码。题目集
0x00 基本算法
递推与递归
95.费解的开关
#include<bits/stdc++.h>
using namespace std;
int temp[10][10],org[10][10];
void opr(int i,int j){
temp[i][j]^=1;
temp[i-1][j]^=1;
temp[i+1][j]^=1;
temp[i][j-1]^=1;
temp[i][j+1]^=1;
}
int main(){
int n,ans,sum,i,j,k;char c;
scanf("%d",&n);
while(n--){
getchar();
for(i=1;i<=5;i++){
for(j=1;j<=5;j++){
scanf("%c",&c);
org[i][j]=c-'0';
}
getchar();
}
//for(i=1;i<=5;i++){for(j=1;j<=5;j++)printf("%d ",org[i][j]);puts("");}
sum=7;
for( i=1;i<=(1<<5);i++){
for( j=1;j<=5;j++)for( k=1;k<=5;k++)temp[j][k]=org[j][k];
ans=0;
for(j=1;j<=5;j++){
if(i>>(j-1)&1)ans++,opr(1,j);
}//第一排的初始化
for( j=2;j<=5;j++){
for( k=1;k<=5;k++){
if(temp[j-1][k]==0)ans++,opr(j,k);
}
if(ans>=sum)break;
}
if(j!=6)continue;
for(k=1;k<=5;k++){
if(temp[5][k]!=1)break;
}
if(k==6)sum=min(sum,ans);
}
if(sum<7)printf("%d\n",sum);
else printf("-1 \n");
}
return 0;
}
103.电影
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+50;
int s[maxn],c[maxn],t[maxn],b[3*maxn],sen[3*maxn],lang[3*maxn],k=0,tot=0;
int find (int x){
return lower_bound(b+1,b+1+k,x)-b;//找到a在b中的编号
}
int main()
{
int n,m;scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&s[i]);
lang[++tot]=s[i];
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d",&c[i]);
lang[++tot]=c[i];
}
for(int i=1;i<=m;i++){
scanf("%d",&t[i]);
lang[++tot]=t[i];
}//所有语言输入完毕
sort(lang+1,lang+tot+1);
for(int i=1;i<=tot;i++){
if(i==1||lang[i]!=lang[i-1])b[++k]=lang[i];
}//一波离散化和统计
for(int i=1;i<=n;i++)sen[find(s[i])]++;
int ans0,ans1,ans2;
ans1=ans0=ans2=0;
for(int i=1;i<=m;i++){
int ansx=sen[find(c[i])],ansy=sen[find(t[i])];
if(ansx>ans1||(ansx==ans1&&ansy>ans2)){
ans1=ansx,ans2=ansy;ans0=i;
}
}
//ans0==0?printf("1\n"):printf("%d\n",ans0);
if(ans0==0)printf("1\n");else printf("%d\n",ans0);
return 0;
}
贪心
115.给树染色
首先可以看出根节点一定是第一个染色,它是所有结点的父节点,毋庸置疑。经过数学分析发现要找到某个权值最大的点染色就必须先给它的父节点染色,所以我们可以将父节点和子节点平均得到一个合并的结点来代替子节点的位置,然后循环刚刚的操作继续找权值更大的。
#include<bits/stdc++.h>
using namespace std;
#define N 10010
#define ll long long
struct node{
double w;//只是起到判断作用
ll fa,c,t;
}Node[N];
int n,r,x,y,father, now;ll ans;
int find()
{
double maxn=0;int ant;
for(int i=1;i<=n;i++){
if(i!=r&&Node[i].w>maxn){//找到最大的权值
maxn=Node[i].w;
ant=i;
}
}
return ant;//返回此时权值最大的编号
}
int main()
{
while(~scanf("%d%d",&n,&r)&&(n||r)){
for(int i=1;i<=n;i++){
scanf("%d",&Node[i].c);
Node[i].w=Node[i].c;
Node[i].t=1;
ans+=Node[i].c;//每个结点自己染色的此数都会加起来,先加个和。
}
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
Node[y].fa=x;
} //初始化
for(int i=1;i<=n-1;i++){
now=find();
Node[now].w=0;//已经合并在一起了,子节点消失了已经
father=Node[now].fa;
ans+=Node[now].c*Node[father].t;
Node[father].c+=Node[now].c;
Node[father].t+=Node[now].t;
Node[father].w=(double)Node[father].c/Node[father].t;
for(int i=1;i<=n;i++)if(Node[i].fa==now)Node[i].fa=father;//子节点的信息全合并到父节点上
}
printf("%d\n",ans);
}
return 0;
}
总结与练习
120.防线
前缀和和二分的思想,注意每一个防线其实只是提供一个盾牌,所以,需要加和。因为题目给定的条件就是说只可能出现一个奇数,所以二分就行
#include<bits/stdc++.h>
using namespace std;
const int maxn=200001;
#define ll long long
ll t, n;
struct node{
ll s,e,d;
}a[maxn];
ll judge(ll x){
ll ans=0;
for(int i=1;i<=n;i++){
if(a[i].s<=x)ans+=(min(x,a[i].e)-a[i].s)/a[i].d+1;
}
return ans;
}
int main(){
ll l,r,mid;
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&a[i].s,&a[i].e,&a[i].d);
}
l=0,r=(1<<31)-1;
while(l<r){
mid=l+r>>1;
if(judge(mid)&1)r=mid;
else l=mid+1;
}
ll ans=judge(r)-judge(r-1);
if(ans&1)printf("%lld %lld\n",r,ans);
else printf("There's no weakness.\n");
}
return 0;
}
121.赶牛入圈
二分+双指针判断
#include<bits/stdc++.h>
using namespace std;
const int maxn=505;
pair<int ,int >a[maxn];
int c,n;
bool cmp(pair<int,int>a,pair<int,int>b)
{
if(a.first==b.first)return a.second<b.second;
else return a.first<b.first;
}
inline int check(int now)
{
for(int i=1;i<=n;i++){
int j=0;
while(j<=n&&(a[j].first-a[i].first)<=now)j++;//其实已经排除了i前面的一些数据
j--;
if(j-i+1<c)return false;//前边就不行了,后边就更不行了。
for(int k=i;k<=j;k++){
int l=a[k].second,r=a[k].second+now,ans=0;
for(int x=i;x<=j;x++){
if(a[x].second<=r&&a[x].second>=l)ans++;
}
if(ans>=c)return true;
}
}
return false;
}
int main()
{
scanf("%d%d",&c,&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].first,&a[i].second);
}
sort(a+1,a+n+1);
int l=0,r=10000,mid;
while(l<r){
mid=(l+r)>>1;
if(check(mid-1))r=mid;//比如说7,8,9这有三个单位,但是9-7=2
else l=mid+1;
}
printf("%d",r);
return 0;
}
122.糖果传递
这里是将小孩子传递的糖果用公式来表示,从而将这个题目转化成一道中位数的题目
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+9;
#define ll long long
ll a[maxn],c[maxn];
int main(){
int n;ll sum=0,ans=0;scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);sum+=a[i];
}
sum/=n;
for(int i=1;i<=n;i++){
c[i]=a[i]+c[i-1]-sum;//就是这个公式
}
sort(c+1,c+n+1);
for(int i=1;i<=n;i++)ans+=abs(c[i]-c[(n+1)>>1]);
printf("%lld",ans);
return 0;
}
123.士兵
中位数概念,注意就是相邻排序就是得让他们有个顺序
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=10050;
ll x[maxn],y[maxn];
int main()
{
int n;ll ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&x[i],&y[i]);
}
sort(x+1,x+1+n);
sort(y+1,y+n+1);
for(int i=1;i<=n;i++)x[i]-=i;
sort(x+1,x+1+n);
for(int i=1;i<=n;i++){
ans+=abs(x[i]-x[(n+1)>>1]);
ans+=abs(y[i]-y[(n+1)>>1]);
}
printf("%lld",ans);
return 0;
}
124.数的进制转换
主要就是高精度这块,可以利用短除法将进制转换换个形式转换
#include<bits/stdc++.h>
using namespace std;
int a,b;
const int maxn=1e5+5;
int t[maxn],A[maxn];char str1[maxn],str2[maxn];
void solve()
{
int len=strlen(str1),k=0;
for(int i=0;i<len;i++){
t[len-i-1]=str1[i]-(str1[i]<58?48:str1[i]<97?55:61);//倒着转换好十进制
}
while(len){
for(int i=len;i>=1;i--){
t[i-1]+=t[i]%b*a;//原先是a进制,补齐
t[i]/=b;//转换成b进制
}
A[k++]=t[0]%b;
t[0]/=b;
while(len&&!t[len-1])--len;//可能当时的最高位仍然大于m,那么就得重复一遍len也不改变
}
str2[k]=NULL;//终止
for(int i=0;i<k;i++)str2[k-1-i]=A[i]+(A[i]<10?48:A[i]<36?55:61);
}
int main()
{
int n;scanf("%d",&n);
while(n--){
scanf("%d%d",&a,&b);
scanf("%s",str1);
solve();
printf("%d %s\n%d %s\n\n",a,str1,b,str2);
}
return 0;
}
125. 耍杂技的牛
首先大胆分析一波,这一定是一个排序,也就是说如何构造一个排序,满足题目所给条件,那我们就单个拎出来分析,相邻两头牛什么条件下换比较优,一顿数学分析,发现W+S逆序就得换,那也就是说按照W+S排序,最后求风险值即可。
#include<bits/stdc++.h>
using namespace std;
pair<int,int>cow[100000];
#define ll long long
int main()
{
int n,x,y;scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&x,&y);
cow[i].first=x+y;
cow[i].second=y;
}
sort(cow+1,cow+1+n);//要改前缀和的话应该在排完序之后加上
ll ans=-1e9,sum=0;
for(int i=1;i<=n;i++){
sum-=cow[i].second;
ans=max(ans,sum);
sum+=cow[i].first;
}
printf("%lld",ans);
return 0;
}
126.最大的和
#include<bits/stdc++.h>
using namespace std;
const int maxn=200;
int ans[maxn][maxn],d[maxn][maxn],sum[maxn];
int main()
{
int n,ant=-1e8;scanf("%d",&n);
bool flag=0;
for(int i=1;i<=n;i++){//n行m列
for(int j=1;j<=n;j++){
scanf("%d",&d[i][j]);
if(d[i][j]>=0)flag=1;//判断是否全为负数
ant=max(ant,d[i][j]);
}
}
if(!flag){//全为负数直接输出
printf("%d",ant);
return 0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
ans[i][j]=ans[i-1][j]+d[i][j];//竖着的一维前缀和
}
}
for(int l=1;l<=n;l++){
for(int r=l;r<=n;r++){
int sur=0;
for(int edge=1;edge<=n;edge++){
sur+=ans[r][edge]-ans[l-1][edge];
if(sur<0)sur=0;//加起来的值等于在减小本就小的ant值,所有不要了
ant=max(ant,sur);
}
}
}
printf("%d",ant);
return 0;
}
127.任务
贪心,从大到小进行排序,用机器人去一个个匹配任务,时间优先。最长时间最大等级所能满足的机器人一定也会满足其他任务,注意数组范围,以及 ans的取值
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
pair<int,int>goal[maxn],robot[maxn];
int cnt[maxn];
#define ll long long
#define pii pair<int,int>
int cmp(pii x,pii y){
if(x.first==y.first)return x.second>y.second;
return x.first>y.first;
}
int main()
{
int n,m;ll num=0;ll ans=0;
scanf("%d",&m);scanf("%d",&n);
for(int i=1;i<=m;i++)scanf("%d%d",&robot[i].first,&robot[i].second);
for(int i=1;i<=n;i++)scanf("%d%d",&goal[i].first,&goal[i].second);
sort(robot+1,robot+m+1,cmp);//按时间和等级排序由大到小
sort(goal+1,goal+n+1,cmp);
int j=1;
for(int i=1;i<=n;i++){
while(j<=m&&goal[i].first<=robot[j].first){//因为时间最长的时候,利润最大
cnt[robot[j].second]++;//先给机器人等级归个类
j++;
}//因为是倒着的排序,所以对后面的来说就更加能够满足了
for(int k=goal[i].second;k<=100;k++){
if(cnt[k]){
num++;
cnt[k]--;
ans+=500*goal[i].first+2*goal[i].second;
break;
}
}
}
printf("%lld %lld\n",num,ans);
return 0;
}
0x10 基本数据结构
0x11 栈
41.包含min函数的栈
class MinStack {
public:
/** initialize your data structure here. */
stack<int>b,c;
MinStack() {
}
void push(int x) {
c.push(x);
if(b.empty()||x<=b.top())b.push(x);
}
void pop() {
if(b.top()==c.top())b.pop();
c.pop();
}
int top() {
return c.top();
}
int getMin() {
return b.top();
}
};
128.编辑器
这个题目频繁遇到段错误问题,后来发现了好几个原因:
1.栈为空的时候不需要处理
2.数组越界
3.开long long
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
stack<int>first;
stack<int>second;
ll sum[1000005],f[1000005];
int main()
{
int Q,k,x;scanf("%d",&Q);char c;
int p=0;sum[0]=0;f[0]=-10000;
while(Q--)
{
getchar();
scanf("%c",&c);
if(c=='I'){
scanf("%d",&x);
first.push(x);
p++;
sum[p]=sum[p-1]+1ll*x;//前缀和
f[p]=max(f[p-1],sum[p]);//找到前缀和最大
}
else if(c=='D')
{
if(first.size()){
first.pop();
p--;
}
}
else if(c=='L')
{
if(first.size()){
int now=first.top();
first.pop();
second.push(now);
p--;
}
}
else if(c=='R')
{
//int now=second.top();
if(second.size()){
int now=second.top();
second.pop();
first.push(now);
p++;
sum[p]=sum[p-1]+1ll*now;
f[p]=max(f[p-1],sum[p]);
}
}
else if(c=='Q'){
scanf("%d",&k);
printf("%lld\n",f[k]);
}
}
return 0;
}
129.火车进栈
我感觉我的搜索又忘光了,wuwuwuuwu(╬ ̄皿 ̄),改天强化一下。
#include<bits/stdc++.h>
using namespace std;
vector<int>path;
stack<int>stk;
int remain=20,n;
void dfs(int u){
if(!remain)return;//就返回20种路径就好了
if(path.size()==n){
remain--;
for(auto x:path){
printf("%d",x);
}
puts("");
}
if(stk.size()){//当前栈里有列车就可以进行出栈
int a=stk.top();
path.push_back(a);
stk.pop();
dfs(u);//注意此时并没有新添加任何新的列车,仍属于dfs(u)的状况
stk.push(a);//或者不出栈
path.pop_back();
}
if(u<=n){
stk.push(u);
dfs(u+1);
stk.pop();//也可以选择暂时不入栈,
}
}
int main()
{
scanf("%d",&n);
dfs(1);
return 0;
}
130.火车进出栈问题
参考题解这里n=60000数据范围比较大,可以用catlan方法优化一下求和f[n]=求和f[k-1]*f[n-k]=C(2n,n)-C(2n,n-1)=C(2n,n)/n-1;
将式子化简一下,得到一个阶乘的形式,毫无疑问会涉及到高精度的问题。新学了一个小技巧压位高精度以及另一个分解质因数求组合数
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll sum[600005],a[1200005],l;
const ll maxn=1e9;//注意1e9是压位的最大值
void prime(int b,int f) {//质因数分解
for (int j = 2; j * j <= b && b; j++) {
while (b % j == 0) {
sum[j] += f;
b /= j;
}
}
if (b)sum[b] += f;
}
void high(ll c){
for(int i=1;i<=l;i++)a[i]*=c;
for(int i=1;i<=l;i++)
a[i+1]+=a[i]/maxn,a[i]%=maxn;
while(a[l+1])++l;//因为压位的缘故,导致可能会出现新的数位,所以需要加上
}//高精度压位
int main()
{
int n;
a[1]=1,l=1;
scanf("%d",&n);
for(int i=1;i<=n;i++)prime(n+i,1);
for(int i=2;i<=n+1;i++)prime(i,-1);//将所得到的每一个质因子个数统计出来
for(int i=2;i<=2*n;i++){//所有统计的质因数都在这个范围里
for(int j=1;j<=sum[i];j++){//用高精度把所有的质因数都乘起来
high(i);
}
}
printf("%lld",a[l]);//最前面的数是不需要前导0的
for(int i=l-1;i>=1;i--){
printf("%09lld",a[i]);
}
return 0;
}
131.直方图中的最大矩形
单调栈里头非常典型的问题
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
long long a[maxn];int l[maxn],r[maxn];
int main()
{
int n;
while(~scanf("%d",&n)&&n){
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
stack<int>s;
for(int i=1;i<=n;i++){
while(!s.empty()&&a[i]<=a[s.top()])s.pop();//栈外到i都是大于a[i]的
if(s.empty())l[i]=1;
else l[i]=s.top()+1;
s.push(i);
}
while(s.size())s.pop();
for(int i=n;i>=1;i--){
while(!s.empty()&&a[i]<=a[s.top()])s.pop();//栈内都是大于a[i]的
if(s.empty())r[i]=n;
else r[i]=s.top()-1;
s.push(i);
}
long long ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,(r[i]-l[i]+1)*1ll*a[i]);
}
printf("%lld\n",ans);
}
return 0;
}
0x12 队列
132. 小组队列
#include<bits/stdc++.h>
using namespace std;
#define maxn 1005
map<int,int>check;
queue<int>q[maxn];
queue<int>now,back;
int main(){
int t,num,x,a,y;
int test=1;
string str;
while(~scanf("%d",&t)&&t){
for(int i=1;i<=t;i++){while(!q[i].empty())q[i].pop(); }
while(!now.empty())now.pop();
for(int i=1;i<=t;i++){
scanf("%d",&num);
for(int j=1;j<=num;j++){
scanf("%d",&x);
check[x]=i;
}
}
while(cin>>str){
if(str=="STOP")break;
if(str=="ENQUEUE"){
scanf("%d",&a);
y=check[a];
if(q[y].empty())now.push(y);
q[y].push(a);
}
else {
y=now.front();
back.push(q[y].front());
q[y].pop();
if(q[y].empty())now.pop();
}
}
printf("Scenario #%d\n",test++);
while(!back.empty()){
printf("%d\n",back.front());
back.pop();
}
puts("");
}
return 0;
}
133.蚯蚓
#include<bits/stdc++.h>
#include<deque>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
ll n,m,u,v,q,x;
int t;
priority_queue<ll>priQ;
queue<ll>Q1;
queue<ll>Q2;//i被切断后,j也被切断,在原始的数据当中,必然是单调递减的,所以就不用对该队列进行排序
ll cal(int i){
int a,b,c;a=b=c=-1;int x=-1;
if(!priQ.empty())a=priQ.top()+1ll*i*q;
if(!Q1.empty())b=Q1.front()+1ll*i*q;
if(!Q2.empty()) c=Q2.front()+1ll*i*q;//这个表示的是队列里面应该出现的数,因为有增长的变化
x=max(a,max(b,c));
if(x==a){
priQ.pop();
}
else if(x==b){
Q1.pop();
}
else if(x==c){
Q2.pop();
}
return x;
}
int main(){
double p;
scanf("%lld%lld%lld%lld%lld%d",&n,&m,&q,&u,&v,&t);
p=(double)u/v;
for(int i=1;i<=n;i++){
scanf("%lld",&x);
priQ.push(x);
}
for(int i=1;i<=m;i++){
ll x=cal(i-1);
Q1.push(floor(x*p)-1ll*i*q);//因为队列里的数都是最初始的数据,因为被剪断不加q,所以要减去i*q;
Q2.push(x-floor(x*p)-1ll*i*q);//
if(i%t==0)printf("%lld ",x);
}
printf("\n");
for(int i=1;i<=n+m;i++){//剩下队列当中就是当前所有蚯蚓的长度,全部输出来即可
ll x=cal(m);
if(i%t==0)printf("%lld ",x);
}
return 0;
}
134. 双端队列
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
#define INF 0x3f3f3f3f//无穷大
typedef long long ll;
struct node{
ll value;
int num;
}x[maxn];
bool cmp(node a,node b){
return a.value==b.value?a.num<b.num:a.value<b.value;
}
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&x[i].value),x[i].num=i;
sort(x+1,x+n+1,cmp);
int mini,maxn,ans=1;
int flag=0;ll now=INF;
for(int i=1;i<=n;)
{
int j=i+1;
while(j<n&&x[j].value==x[i].value)j++;
mini=x[i].num;maxn=x[j-1].num;
i=j;
if(!flag){
if(maxn<now)now=mini;//递减now等于更小的数
else {
flag=1;now=maxn;
}
}
else {
if(mini>now)now=maxn;
else {
flag=0;now=mini;ans++;//算的单谷段数是递增后再递减的点个数
}
}
}
printf("%d",ans);
return 0;
}
135. 最大子序和
单调队列
#include<iostream>
#include<cstdio>
#include<deque>
#include<algorithm>
using namespace std;
typedef long long ll;
deque<long long> que;
const int maxn = 3e5 + 5;
ll sum[maxn];
int main() {
ll n, m, x;
scanf("%lld%lld", &n, &m);
que.clear();
sum[0] = 0;
for (int i = 1; i <= n; i++) {
scanf("%lld", &x);
sum[i] = sum[i - 1] + x;
}
ll maxs = sum[1];
que.push_back(0);//注意初始化
for (int i = 1; i <= n; i++) {
while (!que.empty() && i - que.front() >m)que.pop_front();//队头无法满足,弹出,因为这是一个单调递增队列,所以队头部位是最小的数
maxs = max(maxs, sum[i] - sum[que.front()]);
while (!que.empty() && sum[i] <=sum[que.back()])que.pop_back();//要保持当前的递增形式
que.push_back(i);
}
printf("%lld\n", maxs);
return 0;
}
0x13 链表与邻接表
136.邻值查找
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
typedef long long ll;
struct node{
int data;
int num;
node(int _data,int _num):data(_data),num(_num){}
};
vector<node>ans,a;
int p[maxn],l[maxn],r[maxn];
bool cmp(node a,node b){
return a.data<b.data;
}
int main()
{
int n,x;
scanf("%d",&n);
int data=2e9;
a.push_back({-data,0});
for(int i=1;i<=n;i++){
scanf("%d",&x);
a.push_back({x,i});
}
a.push_back({data,n+1});//防止溢出
sort(a.begin(),a.end(),cmp);
for(int i=1;i<=n;i++){
p[a[i].num]=i;//表示的是原来第几号数排序之后的对应的新序号
l[i]=i-1;
r[i]=i+1;
}
for(int i=n;i>=2;i--){
int j=p[i],left=l[j],right=r[j];//原来i位置处排序后的位置,
int lv=abs(a[j].data-a[left].data);
int rv=abs(a[j].data-a[right].data);
if(lv<=rv){
ans.push_back({lv,a[left].num});
}
else ans.push_back({rv,a[right].num});
l[right]=left,r[left]=right;//灵魂,删去j结点。某结点退出当前的队列排序,因为编号j<i,所以删去结点实际上是避免了j>i的情况出现
}
for(int i=ans.size()-1;i>=0;i--){
printf("%d %d\n",ans[i].data,ans[i].num);
}
return 0;
}
0x14 Hash
137.雪花雪花雪花
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int mod=99991;
int nex[maxn],head[maxn],snow[maxn][10];
int tot=0;
int H(int *a){
int sum=0,mul=1;
for(int i=0;i<6;i++){
sum=(sum+a[i])%mod;
mul=(long long)mul*a[i]%mod;
}
return (sum+mul)%mod;
}
bool check(int *a,int *b)
{
for(int i=0;i<6;i++){
for(int j=0;j<6;j++){
bool flag=1;
for(int k=0;k<6;k++){
if(a[(i+k)%6]!=b[(j+k)%6])flag=0;
}
if(flag)return 1;
flag=1;
for(int k=0;k<6;k++){
if(a[(i+k)%6]!=b[(j-k+6)%6])flag=0;
}
if(flag)return 1;
}
}
return 0;
}
bool insert(int *a){
int val=H(a);
for(int i=head[val];i;i=nex[i]){
if(check(snow[i],a))return 1;
}
++tot;
memcpy(snow[tot],a,6*sizeof(int));
nex[tot]=head[val];
head[val]=tot;
return 0;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int a[10];
for(int j=0;j<6;j++)scanf("%d",&a[j]);
if(insert(a)){
printf("Twin snowflakes found.\n");
return 0;
}
}
printf("No two snowflakes are alike.");
return 0;
}
138.兔子与兔子
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
typedef unsigned long long ull;
char s[maxn];
ull p[maxn],f[maxn];
const int hash1=13331;
#define fir(i,a,b) for(int i=a;i<=b;i++)
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>s+1;int m,x1,y1,x2,y2;
cin>>m;int len=strlen(s+1);
p[0]=1;
fir(i,1,len){
f[i]=(f[i-1]*hash1+s[i]-'a'+1);//hash值
p[i]=p[i-1]*hash1;//数量级
}
while(m--)
{
cin>>x1>>y1>>x2>>y2;
if(f[y1]-f[x1-1]*p[y1-x1+1]==f[y2]-f[x2-1]*p[y2-x2+1])cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
139.回文子串的最大长度
#include<bits/stdc++.h>
using namespace std;
#define fir(i,a,b) for(int i=a;i<=b;i++)
#define fic(i,a,b)for(int i=a;i>=b;i--)
typedef unsigned long long ull;
const int hash1=131;
const int maxn=1e6+5;
ull p[maxn],h1[maxn],h2[maxn];
char s[maxn];
void init(){
p[0]=1;
fir(i,1,maxn)p[i]=p[i-1]*hash1;
}
ull Hash1(int l,int r){
return h1[r]-h1[l-1]*p[r-l+1];
}
ull Hash2(int l,int r){
return h2[l]-h2[r+1]*p[r-l+1];//反着的HASH
}
int main()
{
init();
int l,r,ans,num=0;
while(++num){
scanf("%s",s+1);
ans=0;
if(strcmp(s+1,"END")==0)return 0;
int len=strlen(s+1);
h2[len+1]=0;
fir(i,1,len){
h1[i]=h1[i-1]*hash1+s[i]-'a'+1;
}
fic(i,len,1){
h2[i]=h2[i+1]*hash1+s[i]-'a'+1;
}
fir(i,1,len){
l=0,r=min(len-i,i-1);
while(l<r){
int mid=l+r+1>>1;
if(Hash1(i-mid,i-1)==Hash2(i+1,mid+i))l=mid;
else r=mid-1;
}//奇数
ans=max(ans,l<<1|1);//l*2+1
l=0,r=min(len-i+1,i-1);
while(l<r){
int mid=l+r+1>>1;
if(Hash1(i-mid,i-1)==Hash2(i,i+mid-1))l=mid;
else r=mid-1;
}
ans=max(ans,l<<1);//直接*2
}
printf("Case %d: %d\n",num,ans);
}
return 0;
}
140.后缀数组
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5,hash1=131;
typedef unsigned long long ull;
int SA[maxn],Height[maxn],len;
ull p[maxn],hashx[maxn];
char s[maxn];
int Hash(int a,int b){
return hashx[b]-hashx[a-1]*p[b-a+1];//注意是a-1
}
int get_prefix(int a ,int b){
int l=0,r=min(len-a+1,len-b+1);
while(l<r){
int mid=l+r+1>>1;//防止出现死循环的情况
if(Hash(a,a+mid-1)==Hash(b,b+mid-1))l=mid;
else r=mid-1;
}
return l;
}//找到前缀最大长度
bool cmp(int a ,int b){
int l=get_prefix(a,b);
int av=a+l>len?INT_MIN:s[a+l];
int bv=b+l>len?INT_MIN:s[b+l];
return av<bv;
}//比较不相同的元素大小即字典序排列
int main()
{
cin>>s+1;
len=strlen(s+1);
p[0]=1;
hashx[0]=0;
for(int i=1;i<=len;i++){
hashx[i]=hashx[i-1]*hash1+s[i];
p[i]=p[i-1]*hash1;
SA[i]=i;//初始化
}
sort(SA+1,SA+len+1,cmp);
for(int i=1;i<=len;i++)
{
if(i==1)Height[i]=0;
else Height[i]=get_prefix(SA[i],SA[i-1]);
}
for(int i=1;i<=len;i++){
printf("%d ",SA[i]-1);
}
puts("");
for(int i=1;i<=len;i++){
printf("%d ",Height[i]);
}
puts("");
return 0;
}
0x15 字符串
141.周期
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define fir(i, a, b) for(int i=a;i<=b;i++)
const int maxn = 1e6 + 5;
const int hash1 = 131;
char a[maxn];int nex[maxn];int len;
void cal_next()
{
nex[1]=0;
for(int i=2,j=0;i<=len;i++){
while(j>0&&a[i]!=a[j+1])j=nex[j];
if(a[i]==a[j+1])j++;//可能出现j=0的情况,所以要加条件
nex[i]=j;
}//自我匹配
}
int main(){
//ios::sync_with_stdio(false);
//cin.tie(0);
int cnt=0;
while(cin>>len&&len) {
cnt++;
cin>>a+1;
cal_next();
printf("Test case #%d\n",cnt);
for(int i=2;i<=len;i++){
if(i%(i-nex[i])==0&&i/(i-nex[i])>1)printf("%d %d\n",i,i/(i-nex[i]));
}
puts("");
}
return 0;
}
0x16 Trie
142.前缀统计
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int trie[maxn][26];
int ende[maxn],tot=1;
char s[maxn];
void insert(char *s){
int len=strlen(s);int p=1;
for(int i=0;i<len;i++ )
{
int ch=s[i]-'a';
if(trie[p][ch]==0)trie[p][ch]=++tot;
p=trie[p][ch];
}
ende[p]++;
}
void check(char *s){
int ans=0;
int len=strlen(s);int p=1;
for(int i=0;i<len;i++){
int ch=s[i]-'a';
p=trie[p][ch];
if(!p)break;
ans+=ende[p];
}
printf("%d\n",ans);
return;
}
int main()
{
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
cin>>s;
insert(s);
}
for(int i=1;i<=m;i++){
cin>>s;
check(s);
}
return 0;
}
143. 最大异或对
01字典树 特别注意trie数组的大小,因为只插入0,1,而对于每个数字而言,分成二进制,最多可达32位,所以每个数字都有32个数字需要插入,即二维数组要开trie[maxn*32] [2]
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+6;
int trie[maxn*32][2];
int x[maxn],tot=1;
void insert(int x){
int p=1;
for(int i=30;i>=0;i-- )
{
int ch=x>>i&1;
if(trie[p][ch]==0)trie[p][ch]=++tot;
p=trie[p][ch];
}
}
int check(int x){
int ans=0,p=1;
for(int i=30;i>=0;i--){
int ch=x>>i&1;
int t=trie[p][ch^1];
if(!t)p=trie[p][ch];
else p=t,ans|=(1<<i);//不相同才有异或
}
return ans;
}
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&x[i]);
insert(x[i]);
}
int ant=0;
for(int i=1;i<=n;i++){
ant=max(ant,check(x[i]));
}
printf("%d",ant);
return 0;
}
144. 最长异或值路径
这里我们使用链式前向星来维护,因为这是一颗树,所以任何一个结点都可以当做根结点,进行扩展,用一个图来遍历找到所有的点到达根结点的所有异或和,这样a,b两个点路径上的异或和就是刚刚求得a~根结点,b~根结点这两个值的异或和,也就是说可以利用上一题的公式直接求即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+6;
int trie[maxn*32][2];
int x[maxn],f[maxn],tot=0;
void insert(int x){
int p=1;
for(int i=30;i>=0;i-- )
{
int ch=x>>i&1;
if(trie[p][ch]==0)trie[p][ch]=++tot;
p=trie[p][ch];
}
}
int check(int x){
int ans=0,p=1;
for(int i=30;i>=0;i--){
int ch=x>>i&1;
int t=trie[p][ch^1];
if(!t)p=trie[p][ch];
else p=t,ans|=(1<<i);//不相同才有异或
}
return ans;
}
int head[2*maxn],ver[2*maxn],vis[maxn],edge[2*maxn],nex[2*maxn];
void add(int u,int v,int w){
ver[++tot]=v;edge[tot]=w;
nex[tot]=head[u],head[u]=tot;//即ver[head[u]]=v,edge[head[u]=w;遍历nex[nex]排列顺序
}
void dfs(int x){
vis[x]=true;
for(int i=head[x];i;i=nex[i])//找到所有能走的点
{
if(vis[ver[i]]==true)continue;//单向,走了的就不去走了
else f[ver[i]]=f[x]^edge[i];
dfs(ver[i]);//一直往后边跑,跑到走不动为止
}
}
int main()
{
int n,u,v,w;scanf("%d",&n);
for(int i=1;i<=n-1;i++){
cin>>u>>v>>w;
u++;v++;
add(u,v,w);
add(v,u,w);
}
dfs(1);tot=1;
for(int i=1;i<=n;i++){
insert(f[i]);//求出x数组
}
int ant=0;
for(int i=1;i<=n;i++){
ant=max(ant,check(f[i]));
}
printf("%d",ant);
return 0;
}
0x17 二叉堆
145.超市
在这里,我用priority_queue<int,vector
,greater >q;来表示最小堆的时候出现了问题TLE了,我也不知道为啥。所以就把负数放进最小堆里边,最后把输出的数变成正数就可以了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5;
struct node{
int p,d;
bool operator< (const node &x) const {
return x.d>d;
}
}cas[maxn];//按照d大小排序
//priority_queue<int,vector<int>,greater<int>>q;//小根堆
priority_queue<int>q;
int main()
{
int n;
while(scanf("%d",&n)!=EOF){
if(n==0){
printf("0\n");
continue;
}
for(int i=1;i<=n;i++){
scanf("%d%d",&cas[i].p,&cas[i].d);
}
sort(cas+1,cas+n+1);
for(int i=1;i<=n;i++){
if(cas[i].d==q.size()){
if(-cas[i].p<q.top()){
q.pop();
q.push(-cas[i].p);
}
}
else if(cas[i].d>q.size())q.push(-cas[i].p);
}
int ans=0;
while(q.size()){ans-=q.top(),q.pop();}
printf("%d\n",ans);
}
return 0;
}
146.序列
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+5;
int temp[105][maxn],now[maxn],ans[maxn],k;
struct node{
int i,j,last;//last是为了判断前面那个是i+1还是j+1的结果
friend bool operator <(node a,node b){
return temp[k][a.j]+now[a.i]>temp[k][b.j]+now[b.i];//按照A[i]+B[i]的顺序排列
}
};
priority_queue<node>q,kong;//最小堆
int main()
{
int t, m,n;;scanf("%d",&t);
while(t--){
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++)scanf("%d",&temp[i][j]);
sort(temp[i]+1,temp[i]+n+1);
}
for(int i=1;i<=n;i++)now[i]=temp[1][i];
for(k=2;k<=m;k++) {
q = kong;
q.push({1, 1, false});
for (int i = 1; i <= n; i++) {
node fon = q.top();
q.pop();
ans[i] = now[fon.i] + temp[k][fon.j];
q.push({fon.i+1 , fon.j, true});
if (fon.last == false)q.push({fon.i, fon.j+1 , false});//f->tf,ff
}
for (int i = 1; i <= n; i++) {
now[i] = ans[i];
}
}
for(int i=1;i<=n;i++){
printf("%d ",now[i]);
}
puts("");
}
return 0;
}
147.数据备份
优先队列,链表,贪心
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5;
const int inf=0x3f3f3f3f;
int a[maxn],del[maxn],pre[maxn],nex[maxn];
struct cmp{
bool operator()(int x,int y){
return a[x]>a[y];
}
};
priority_queue<int ,vector<int>,cmp>q;
int main()
{
int n,k;scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(i!=1){
a[i-1]=a[i]-a[i-1];
}
}
n--;a[0]=inf;//无穷大的数,防止越界,
for(int i=1;i<=n;i++){
q.push(i);
pre[i]=i-1;
nex[i]=i+1>n?0:i+1;
}
long long ans=0;
for(int i=1;i<=k;i++){
int x=q.top();q.pop();
while(del[x]){
x=q.top();
q.pop();
}//删除掉已经不存在的节点
ans+=a[x];
a[x]=a[pre[x]]+a[nex[x]]-a[x];
del[pre[x]]=1;del[nex[x]]=1;//删除
pre[x]=pre[pre[x]];nex[x]=nex[nex[x]];
nex[pre[x]]=x;pre[nex[x]]=x;
q.push(x);
}
printf("%lld",ans);
return 0;
}
148.合并果子
k=2的哈夫曼编码
#include<bits/stdc++.h>
using namespace std;
struct cmp{
bool operator ()(int a,int b){
return a>b;
}
};
priority_queue<int,vector<int>,cmp>q;//最小堆
int main()
{
int ans=0,n,a,k;scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a),q.push(a);
while(1){
int x=q.top();q.pop();
if(q.size()==0){
break;
}
x+=q.top();q.pop();
ans+=x;
q.push(x);
}
printf("%d",ans);
return 0;
}
149.荷马史诗
类比推理成哈夫曼编码,注意为使得编码长度最短,应该优先合并深度低的数字
#include<bits/stdc++.h>
using namespace std;
#define pll pair<long long,long long>
struct cmp{
bool operator() (pll a,pll b){
if(a.first==b.first)return a.second>b.second;
else return a.first>b.first;
}
};
priority_queue<pll,vector<pll>,cmp>q;//最小堆
int main()
{
long long ans=0,deep=0,a;int n,k;scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%lld",&a),q.push({a,0});
while((n-1)%(k-1)!=0)q.push({0,0}),n++;
while(1){
pll x=q.top();q.pop();
if(q.size()==0){
break;
}
for(int i=1;i<=k-1;i++){
x.first+=q.top().first;
x.second=max(x.second,q.top().second);
q.pop();
}
deep=max(x.second+1,deep);
ans+=x.first;
q.push({x.first,x.second+1});
}
printf("%lld\n%lld",ans,deep);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具