洛谷做题笔记(更新ing

文章目录

    • 排序
    • 递归递推
    • 模拟
    • 贪心
    • 搜索
    • 数据结构(队列、集合、map)
    • 二分
    • ST表
    • 分治
    • 二叉树
    • 链表
    • 差分与前缀和
    • 线段树


排序

1、拼数
想到用string排序,但是自定函数中写的是a>b
这样会出现321>32但拼起来就不对的情况(应该是32321)
所以自定义函数中要用a+b>b+a
ps看题解的 妙啊

#include
#include
#include
using namespace std;
string str[30];
int comp(string a,string b){return a+b>b+a;}
int main()
{
 int n;
 while(cin>>n)
 {
 for(int i=0;i<n;i++)
 {
  cin>>str[i];
 }
 sort(str,str+n,comp);
 string s="";
 for(int i=0;i<n;i++)
 {
  s+=str[i];
 }
 cout<<s<<endl;
 }
}

2、生日
stable_sort稳定输入:先输入的后输出,就可以不设置level
注意comp的写法

#include
#include
#include
using namespace std;
typedef struct node
{
 string name;
 int year;
 int month;
 int date;
}node;
node nod[105];
int n;
bool comp(node a,node b)
{
 if(a.year!=b.year) return a.year<b.year;
 if(a.year==b.year&&a.month!=b.month) return a.month<b.month;
 if(a.month==b.month&&a.date!=b.date) return a.date<b.date;
}
int main()
{
 cin>>n;
 for(int i=0;i<n;i++)
 {
  cin>>nod[i].name;
  cin>>nod[i].year;
  cin>>nod[i].month;
  cin>>nod[i].date;
 }
 stable_sort(nod,nod+n,comp);
 for(int i=0;i<n;i++)
  cout<<nod[i].name<<endl;
}

3、宇宙总统
因为输入是1~n,所以排序是到n+1;

#include
#include
#include
using namespace std;
int n;
typedef struct node
{
 int number;
 string str;
}node;
node s[25];
int comp(node a,node b)
{
 if(a.str.size()!=b.str.size())
 return a.str.size()>b.str.size();
 else
 return a.str>b.str;
}
int main()
{
 while(cin>>n)
 {
  for(int i=1;i<=n;i++)
  {
   cin>>s[i].str;
   s[i].number=i;
  }
  sort(s+1,s+n+1,comp);
  cout<<s[1].number<<endl;
  cout<<s[1].str<<endl;
 }
}

4、明明的随机数
用set:自动去重,并且set中元素本就是排好序
注意迭代器的使用方法

#include
#include
using namespace std;
int main()
{
 int n;
 set<int> s;
 cin>>n;
 int temp;
 for(int i=0;i<n;i++)
 {
  cin>>temp;
  s.insert(temp);
 }
 set<int>::iterator it;
 cout<<s.size()<<endl;
 for(it=s.begin();it!=s.end();it++)
 {
  cout<<*it<<" ";
 }
}

递归递推


1、p1149-火柴棒等式
递归。
本来写的for循环只有1-9,然后发现错了,加数被加数和都是可以好多位的。然后就犯傻。其实从n<=24,可以推个大概的上限,就是到底可以几位数相加,所以我们把上限设置在1000.
也就是把本来for(int i=0;i<9;i++)改成for(int i=0;i<=1000;i++)就可以了。
而0~1000要用多少根火柴是可以通过递推算出来的。这里的递推看了题解,写得很巧妙——x[i]=x[i/10]+x[i%10]

AC代码:

#include
#include
using namespace std;
int dp[1005]={6,2,5,5,4,5,6,3,7,6};
vector<int> vec;
int n;
int ans=0;
void init()
{
for(int i=10;i<=1000;i++)
 {
  dp[i]=dp[i/10]+dp[i%10];
 }
}
void dfs(int num,/*剩余火柴*/int index/*现在是1/2/3的第几个数字*/)
{
 if(num==0&&index==3)
 {
  if(vec[0]+vec[1]==vec[2])
   ans++;
  return;
 }
 if(index>=3) return;
 if(num<=0) return;
 for(int i=0;i<=1000;i++)
 {
  vec.push_back(i);
  dfs(num-dp[i],index+1);
  vec.pop_back();
 }
}
int main()
{
 init();
 while(cin>>n)
 {
 ans=0;
 if(n<=10)
 {
  cout<<0<<endl;
  break;
 }
 n=n-4;//减去加号和等于号
 dfs(n,0);
 cout<<ans<<endl;
 }
}

2回文质数
玄学题解
1、因为回文数少,质数多。所以先判断回文,再判断质数。
2、偶数位的回文数一定不是质数(除了11)。因为偶数位的回文数一定是11的倍数——据说是小学数学,但我小学数学读不好 所以六位数和四位数的pass了。并且没有比9999999(7个)大的,我也不知道为什么,打个问号在这里了……
3、用printf,比较快
4、直接建数组int a[20],一开始用vector,有三个测试案例过不去。所以说vector耗时啊= =
ac代码:

#include
#include
#include
#include
using namespace std;
int check(int x)
{
 if((x>=1000&&x<=9999)||(x>=100000&&x<=999999))
  return 0;
 else
  return 1;
}
int judge(int n)//判断是不是质数
{
 for(int i=2;i<=sqrt(n);i++)
 {
  if(n%i==0)
   return 0;
 }
 return 1;
}
int hui(int x)//判断回文
{
 int vec[20];int flag=1;
 while(x!=0)
 {
  vec[flag]=x%10;
  x=x/10;
  flag++;
 }
 for(int i=1;i<=flag/2;i++)
 {
  if(vec[i]!=vec[flag-i])
   return 0;
 }
 return 1;
}
int main()
{
 int a;
 int b;
 while(cin>>a>>b)
 {
 b=min(9999999,b);
 if(a%2==0) a++;
 for(int i=a;i<=b;i=i+2)
 {
  if(!check(i)) continue;
     if(!hui(i)) continue;
  if(!judge(i)) continue;
  printf("%d\n",i);
 }
 }
}

3、组合数
不难,但是题目要求“每个数字占三个位
用到iomanip里的setw

#include
#include
#include
#include
int vis[30];
using namespace std;
int n,r;
void dfs(vector<int> vec,int index,int cnt)
{
 if(index==r)
 {
  for(int i=0;i<vec.size();i++)
  {
   cout<<setw(3)<<vec[i];
  }
  cout<<endl;
 }
 for(int i=cnt+1;i<=n;i++)
 {
  if(vis[i]==0)
  {
   vis[i]=1;
   vec.push_back(i);
   dfs(vec,index+1,i);
   vec.pop_back();
   vis[i]=0;
  }
  else
  continue;
 }
}
int main()
{
 while(cin>>n>>r)
 {
 memset(vis,0,sizeof(vis));
 vector<int> vec;
 dfs(vec,0,0);
 }  
}

4、火星人
全排列函数(包含在algorithm头文件里):
如果是vector vec
next_permutation(vec.begin(),vec.end());
如果是数组int a[]
next_permutation(a,a+n)
注意是从当前开始全排列

#include
#include
#include
#include
#include
using namespace std;
int n,m;
int main()
{
 vector<int> vec;
 int temp;
 while(cin>>n>>m)
 {
  for(int i=0;i<n;i++)
  {
   cin>>temp;
   vec.push_back(temp);
  }
  for(int i=1;i<=m;i++)
  {
   next_permutation(vec.begin(),vec.end());
  }
  for(int i=0;i<n;i++)
  {
   if(i!=0) cout<<" ";
   cout<<vec[i];
  }
 }
}

5栈
不用记忆化搜索会TLE
亲测

#include
using namespace std;
int f[20][20];
int n;
int dfs(int x,int y)//x是队列里的数,y是栈里的数
{
 if(f[x][y]!=0) return f[x][y];
 if(x==0) return 1;
 f[x][y]+=dfs(x-1,y+1);//移到栈里
 if(y>0) f[x][y]+=dfs(x,y-1);//从栈里pop
 return f[x][y];
}
int main()
{
 while(cin>>n)
 {
  dfs(n,0);
  cout<<f[n][0]<<endl;
 }
}

dp写法:

#include
#include
using namespace std;
int f[20][20];
int n;
int main()
{
 while(cin>>n)
 {
  memset(f,0,sizeof(f));
  for(int i=1;i<=n;i++)
  {
   f[0][i]=1;
  }
  for(int i=1;i<=n;i++)
  {
   for(int j=0;j<=n-i;j++)
   {
    f[i][j]+=f[i-1][j+1];
    if(j>0) f[i][j]+=f[i][j-1];
   }
  }
  cout<<f[n][0]<<endl;
 }
}
   

6function
1、一开始修改了abc但是!注意输出的时候是要输出原来的abc的……
因为会RE所以如果abc其中有一个小于0就把他们都变成0

#include
#include
long long a,b,c;
int f[25][25][25]={0};
using namespace std;
long long w(long long a,long long b,long long c)
{
 if(f[a][b][c]>0) return f[a][b][c];
 if(a==0||b==0||c==0)
  return f[a][b][c]=1;
 else if(a<b&&b<c) return f[a][b][c]=w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c);
 else return f[a][b][c]=w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1);
}
int main()
{
 while(cin>>a>>b>>c)
 {
  memset(f,0,sizeof(f));
  if(a==-1&&b==-1&&c==-1) break;
  cout<<"w("<<a<<", "<<b<<", "<<c<<") = ";
  if(a<=0||b<=0||c<=0) {a=b=c=0;}
  else if(a>20||b>20||c>20) {a=b=c=20;}
  cout<<w(a,b,c)<<endl;
 }
}

7、蜜蜂路线
dp+高精度
简单的dp,难在于要用高精度处理。

#include
using namespace std;
int dp[1005][600];//600存储高精度数字
int main()
{
 int m,n;
 while(cin>>m>>n)
 {
  memset(dp,0,sizeof(dp));
  dp[m][0]=1;
  dp[m+1][0]=1;
  for(int i=m+2;i<=n;i++)
  {
   for(int j=0;j<600;j++)
    {
     dp[i][j]+=dp[i-1][j]+dp[i-2][j];
     if(dp[i][j]>9)
     { 
      dp[i][j]=dp[i][j]%10;
      dp[i][j+1]++;
     }
    }
  }
  int flag=0;
  for(int i=600;i>=0;i--)
  {
   if(flag==0&&dp[n][i]==0) continue;//flag标志位看是否到首位
   else flag=1;
   cout<<dp[n][i];
  }
 }

//类似题:数楼梯 但是这题要考虑n等于0的情况。注意!如果只有80分,可能是数组开的不够大。或者没考虑特殊情况。

8、小A点菜
一直在想怎么用记忆化搜索 其实剪枝就可以了……
dfs:

#include
using namespace std;
int n,m;
int price[105];
int ans=0;
void dfs(int index,int sum)
{
 if(sum>m) return;//剪枝
 if(sum==m) {
  ans++;
  return;
 }
 if(index==n) return;
 for(int i=index;i<n;i++)
 {
  dfs(i+1,sum+price[i]);
 }
}
int main()
{
 while(cin>>n>>m)
 {
  ans=0;
  for(int i=0;i<n;i++)
  {
   cin>>price[i];
  }
  dfs(0,0);
  cout<<ans<<endl;
 }
}

9、八皇后
经典回溯问题,设置三个数组用来判断列和两个对角线

#include
#include
using namespace std;
#define maxn 15
int visl[maxn*maxn];
int visx[maxn*maxn];
int visy[maxn*maxn];
vector<int> vec;
int ans=0;
int count=0;
int n;
void dfs(int x)
{
 if(x>n) 
 {
  ans++;
  count++;
  if(count<=3)
  {
   for(int i=0;i<vec.size();i++)
   { 
    if(i!=0) cout<<" ";
    cout<<vec[i];
   }
   cout<<endl;
  }
  return ;
 }
 for(int y=1;y<=n;y++)
 {
  if(visl[y]==0&&visx[x+y]==0&&visy[x-y+n]==0)
  {
   visl[y]=visx[x+y]=visy[x-y+n]=1;
   vec.push_back(y);
   dfs(x+1);
   vec.pop_back();
   visl[y]=visx[x+y]=visy[x-y+n]=0;
  } 
 }
}
int main()
{
 cin>>n;
 dfs(1);
 cout<<ans<<endl;
}

滑雪
记忆化搜索

#include
#include
using namespace std;
int dp[105][105];
int map[105][105];
int xt[4]={1,-1,0,0};
int yt[4]={0,0,1,-1};
int vis[105][105];
int n,m;
int dfs(int x,int y)
{
	vis[x][y]=1;
	if(dp[x][y]>1) return dp[x][y];
	dp[x][y]=1;
	for(int i=0;i<4;i++)
	{
		int xx=x+xt[i];
		int yy=y+yt[i];
		if((xx>0)&&(yy>0)&&(xx<=n)&&(yy<=m)&&(map[xx][yy]>map[x][y]))//可以往更高的地方爬
		{
			dp[x][y]=max(dp[x][y],dfs(xx,yy)+1);
		}
	}
	return dp[x][y];
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>map[i][j];
			
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(vis[i][j]==0)
				dfs(i,j);
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			ans=max(ans,dp[i][j]);
		}
	}
	cout<<ans<<endl;
}

模拟


1、铺地毯

虽然题目只有一组数据,但是不用while(cin>>)循环输入就RE了T T
想法是用栈,最后覆盖上去的就是第一个出来的,符合坐标在里面的就可以

#include
#include
using namespace std;
int n;
typedef struct node{
 int index;
 int x;
 int y;
 int xlength;
 int ylength;
}node;
int main()
{
 while(cin>>n)
 {
 stack<node> s;
 node temp;
 int a;
 int b;
 for(int i=1;i<=n;i++)
 {
  temp.index=i;
  cin>>temp.x;
  cin>>temp.y;
  cin>>temp.xlength;
  cin>>temp.ylength;
  s.push(temp);
 }
 cin>>a>>b;
 int xmin,xmax,ymin,ymax;
 int flag=0;
 while(s.size()!=0)
 {
  temp=s.top();
  s.pop();
  xmin=temp.x;
  ymin=temp.y;
  xmax=temp.x+temp.xlength;
  ymax=temp.y+temp.ylength;
  if(a>=xmin&&a<=xmax&&b<=ymax&&b>=ymin)
  {
   cout<<temp.index<<endl;
   flag=1;
   break;
  }
 }
 if(flag==0) cout<<-1<<endl;
 }
}

2、均分纸牌
依然是看题解。
找到特殊的点:最左边和最右边——最左边只能通过右边移给它,也就是说它达到平均数以后就不用再动了。当第一张牌达到平均数,我们就直接剔除,然后看第二堆开始的后面的牌堆。
注意,在此时,我们并不关心,是先从第三堆以后的移到第二堆再给第一堆还是先给第一堆再从后面的补给,因为其实都是一样的。
而每个变成第一堆的第二三四五堆,只要是已经达到平均数的,都不用再动它,也就不用++ans。

#include
#define maxn 10005
using namespace std;
int n;
int a[maxn];
int main()
{
 while(cin>>n)
 {
  int sum=0;
  int ans=0;
  for(int i=1;i<=n;i++)
  {
   cin>>a[i];
   sum+=a[i];
  }
  int average=sum/n;
  for(int i=1;i<n;i++)
  {
   if((a[i]-average)!=0)
   {
    a[i+1]+=a[i]-average;
    ans++;
   }
  }
  cout<<ans<<endl;
 }
}



stl提供了堆,所以一般不用手写
priority_queue< int > 最大堆
priority_queue,greater< int > >最小堆
求第k小的值
代码戳链接
这道题目就是用最小堆和最大堆两个堆来实现
合并果子
先取出最小的两堆合并成一堆,里面涉及哈夫曼树以及堆的使用,可以使权值最小。
priority:参数中greater< int>可以变成升序堆,每次都取最小的两个 priority_queue,greater< int> >

#include
#include
using namespace std;
int main()
{
 int n;
 int temp;
 while(cin>>n)
 {
  priority_queue<int,vector<int>,greater<int> > que;
  for(int i=0;i<n;i++)
  {
   cin>>temp;
   que.push(temp);
  }
  int a;
  int b;
  int ans=0;
  for(int i=1;i<=n-1;i++)
  {
   a=que.top();que.pop();
   b=que.top();que.pop();
   temp=a+b;ans+=temp;que.push(temp);
  }
  cout<<ans<<endl;
 }
}

贪心


铺设道路
和2013年的积木解题一模一样

#include
using namespace std;
#define maxn 100000
int a[maxn];
int main()
{
 int n;
 while(cin>>n)
 {
  int sum=0;
  for(int i=0;i<n;i++)
  cin>>a[i];
  for(int i=1;i<n;i++)
  {
   if(a[i]>a[i-1])
   {
    sum+=a[i]-a[i-1];
   }
  }
  sum+=a[0];
  cout<<sum<<endl;
 }
 } 

搜索


1、马的遍历
bfs搜索,记录状态。
答案格式:左对齐五位:<

#include
#include
#include
#include
#define maxn 405
using namespace std;
typedef struct node
{
 int x;
 int y;
 int layer;
}node;
int xm,ym;
int n,m;
queue<node> que;
int map[maxn][maxn];
int vis[maxn][maxn];
int xt[8]={-1,-2,-2,-1,1,2,2,1};
int yt[8]={-2,-1,1,2,2,1,-1,-2};
void bfs(int x,int y)
{
 node temp;
 temp.x=x;
 temp.y=y;
 temp.layer=0;
 que.push(temp);
 vis[x][y]=1;
 while(que.size()!=0)
 {
  temp=que.front();
  que.pop();
  map[temp.x][temp.y]=temp.layer;
  for(int i=0;i<8;i++)
  {
   int xx=temp.x+xt[i];
   int yy=temp.y+yt[i];
   if(xx>0&&xx<=n&&yy>0&&yy<=m&&vis[xx][yy]==0)
   {
    vis[xx][yy]=1;
    node a;
    a.x=xx;
    a.y=yy;
    a.layer=temp.layer+1;
    que.push(a);
   }
  }
 }
}
int main()
{
 cin>>n>>m>>xm>>ym;
 memset(map,-1,sizeof(map));
 bfs(xm,ym);
 for(int i=1;i<=n;i++)
 {
  for(int j=1;j<=m;j++)
  {
   cout<<std::left<<setw(5)<<map[i][j];
  }
  cout<<endl;
 }
}

2、
填涂颜色
为了防止最边角的0搜不到,在外面加一整圈0,从0~n+1开始

#include
using namespace std;
int map[35][35];
int vis[35][35];
int n;
int xt[4]={-1,0,0,1};
int yt[4]={0,1,-1,0};
void dfs(int x,int y)
{
 if(x<0||y<0||x>n+1||y>n+1||map[x][y]==1)
  return;
 else
 {
  map[x][y]=0;
  vis[x][y]=1;
  for(int i=0;i<4;i++) 
  {
    if(vis[x+xt[i]][y+yt[i]]==0)
    {
     dfs(x+xt[i],y+yt[i]);
    }
  }
 }
 
}
int main()
{
 cin>>n;
 memset(map,0,sizeof(map));
 for(int i=1;i<=n;i++)
 {
  for(int j=1;j<=n;j++)
  {
   cin>>map[i][j];
   if(map[i][j]==1) continue;
   else map[i][j]=2;
  }
 }
 dfs(0,0);
 for(int i=1;i<=n;i++)
 {
  for(int j=1;j<=n;j++)
   cout<<map[i][j]<<" ";
  cout<<endl;
 }
}
 

3、字符串变换
1、用map判断是否出现过这个字符串
如果是数字用 vis数组,如果是字符串,就用map
2、记住判断这个子串是否出现过以后,后面也有可能出现,所以要遍历直到find()超出字符串的范围
3、注意字符串的用法

#include
#include
#include
#include
#include
#include
using namespace std;
string str[100];
string ptr[100];
typedef struct node{
 string t;
 int layer;
}node;
map<string,int> m;
int vis[25];
int main()
{
 string a,b;
 cin>>a>>b;
 queue<node> que;
 node nod;
 nod.t=a;
 nod.layer=0;
 que.push(nod);
 string s,p;
 int index=0;
 m[a]++;
 while(cin>>s>>p)
 {
  str[index]=s;
  ptr[index]=p;
  index++;
 }
 memset(vis,0,sizeof(0));
 vector<string> vec;
 vec.push_back(a);
 while(que.size()!=0)
 {
  node temp=que.front();
  que.pop();
  if(temp.t==b) {
    if (temp.layer > 10 || temp.layer == 0)
     cout << "NO ANSWER!" << endl;
    else
     cout<<temp.layer<<endl;
    return 0;
  }
  for(int i=0;i<index;i++)
  {
   for(int it=temp.t.find(str[i]);it<temp.t.length();it=temp.t.find(str[i],it+1))
   {
   string ss=temp.t;
   if(it<temp.t.length())
   {
    ss.replace(it,str[i].size(),ptr[i]);
    if(m[ss]!=0) continue;
    m[ss]++;
    node nd;
    nd.t=ss;
    nd.layer=temp.layer+1;
    que.push(nd);
    vec.push_back(ss);
   }
   }
  }
 }
 cout<<"NO ANSWER!"<<endl;
}

数据结构(队列、集合、map)


约瑟夫问题-队列
如果取出的数刚好报m,扔掉,不然就放入队列中准备下一次报数

#include
#include
using namespace std;
int n,m;
int main()
{
	cin>>n>>m;
	queue que;
	for(int i=1;i<=n;i++)
	{	
		que.push(i);
	}
	int cnt=1;
	while(que.size()!=0)
	{
		int temp=que.front();
		que.pop();
		if(cnt==m) {cout<

木材仓库-集合
1、set的查找:find()
2、迭代器iterator 注意set中本就是排好位置顺序的
3、插入 insert()
4、判断是否为空empty();
5、删除:erase
set用法介绍

#include
#include
using namespace std;
int n;
int main()
{
	cin>>n;
	int a,b;
	set<int> s;
	set<int>::iterator it;
	while(n--)
	{
		cin>>a>>b;
		if(a==1)//放木柴
		{
			if(s.find(b)==s.end())//没有这个木柴
				s.insert(b);
			else
				cout<<"Already Exist"<<endl;
		}
		if(a==2)//放木柴
		{
			if(s.empty()){cout<<"Empty"<<endl; continue;}
			it=s.begin();
			while((it!=s.end())&&(*it)<b) it++;
			if(*it==b) {s.erase(*it);cout<<b<<endl; continue;}
			if(it==s.end()){it--;cout<<*it<<endl; s.erase(*it);continue;}
			if(it==s.begin()){cout<<*it<<endl; s.erase(*it);continue;}
			int aft=*it-b;
			it--;
			int bef=b-*it;
			if(bef<=aft)//输出小的,差距一样输出小的
			{
				cout<<*it<<endl;
				s.erase(*it);
				continue;
			}
			else
			{
				it++;
				cout<<*it<<endl;
				s.erase(*it);
				continue;
			}
		}
	}
}

a-b数对-map
先用map记a[i]中的所有数,然后-c,在计算一遍

#include
#include
using namespace std;
#define maxn 200005
long long n,c;
long long a[maxn];
map<long long,long long> m;
int main()
{
	cin>>n>>c;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
		m[a[i]]++;
		a[i]-=c;
	}
	long long ans=0;
	for(int i=0;i<n;i++)
	{
		ans+=m[a[i]];
	}
	cout<<ans<<endl;
}

不重复数字-unordered_map 的定义与用法都与 map 差不多,只不过是用Hash来存储的,判断是O(1)的。所以判断来说比较快。
以及要用快读不然过不了

#include
#include
#include
using namespace std;
long long n;
inline int read()
{
    char c=getchar();int x=0,f=1;
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=x*10+c-48;
    return x*f;
}
int main()
{
	cin>>n;
	while(n--)
	{
		unordered_map<int,int> m;
		int cnt;
		cin>>cnt;
		for(int i=0;i<cnt;i++)
		{
			int temp=read();
			if(m[temp]==0) cout<<temp<<" ";
			m[temp]=1;
		}
		cout<<endl;
	}
}

二分


砍树

#include
#include
#include
#define maxn 1000005
using namespace std;
long long n;long long m;long long lowest;long long highest;long long mid;
long long a[maxn];
int main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=0;i<n;i++)
	{
		scanf("%lld",&a[i]);
		highest=max(highest,a[i]);
	}
	while(lowest<=highest)
	{
		mid=(lowest+highest)/2;
		long long ans=0;
		for(int i=0;i<n;i++)
		{
			if(a[i]>mid)
			{
				ans+=a[i]-mid;
			}
		}
		if(ans<m) highest=mid-1;
		else lowest=mid+1;
	}
	cout<<lowest-1<<endl;
}

实数二分

#include
#include
using namespace std;
double a,b,c,d;
double fc(double x)
{
    return a*x*x*x+b*x*x+c*x+d;
}
int main()
{
	cin>>a>>b>>c>>d;
	int cnt=0;
	for(double i=-100;i<100;i++)
	{
		double l=i;
		double r=i+1;
		if(fc(l)==0) {printf("%.2lf ",l);continue;}
		if(fc(l)*fc(r)<0)
		{
			while(r-l>=0.001)
			{
				double mid=(l+r)/2.0;
				if(fc(mid)*fc(r)<=0) l=mid;
				else r=mid;
			}
			cnt++;
			printf("%.2lf ",r);
		}
	if(cnt==3) break;
	}
}

ST表


1、模板
用到了倍增

#include
#include
#include
#include
#define MAXN 1000010//不能写成1e6+10
using namespace std;
int Maxx[MAXN][21];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int query(int l,int r)
{
	int k=log2(r-l+1);
	return max(Maxx[l][k],Maxx[r-1<<k+1][k]);
}
int main()
{
	int n=read();
	int m=read();
	for(int i=1;i<=n;i++) Maxx[i][0]=read();
	for(int i=1;i<=21;i++)
	{
		for(int j=1;j+(1<<i)<=n+1;j++)
		{
			Maxx[j][i]=max(Maxx[j][i-1],Maxx[j+1<<(i-1)][i-1]);
		}
	}
	for(int i=1;i<=m;i++)
    {
        int l=read(),r=read();
        printf("%d\n",query(l,r));
    }
}

分治


逆序
其实就是归并排序,在排序中如果右半部分的先移下来但是左半部分的还有剩下就说明有逆序队。此时左半部分坐标i,那么逆序的个数就是mid-i+1

#include
#include
using namespace std;
#define maxn 500005
long long a[maxn];
long long b[maxn];
long long ans=0;
void msort(int x,int y)
{	
	if(x==y) return;
	int mid=(x+y)/2;
	msort(mid+1,y);
	msort(x,mid);
	int i=x;int j=mid+1;int index=x;
	while(i<=mid&&j<=y)
	{
		if(a[i]<=a[j])
		{
			b[index++]=a[i++];
		}
		else//j比较小,有逆序
		{
			b[index++]=a[j++];
			ans+=mid-i+1;
		}
	}
	while(i<=mid)
		b[index++]=a[i++];
	while(j<=y)
		b[index++]=a[j++];
	for(int i=x;i<=y;i++)
		a[i]=b[i];
}
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		scanf("%lld",&a[i]);
	}
	msort(0,n-1);
	printf("%lld\n",ans);
}

二叉树


二叉树的深度遍历、宽度遍历二叉搜索树(待)、二叉平衡树。已知先序中序求后续遍历等一系列操作……
二叉树题目+题解


链表


一道链表模板题
链表模板


差分与前缀和

差分与前缀和讲解
铺地毯

#include
#include
using namespace std;
int a[1005][1005];
int n,m;//n*n个格子被m个地毯覆盖
int main()
{
	scanf("%d%d",&n,&m);
	int xa,ya,xb,yb;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
		a[xa][ya]++;//从这个点开始改变,因为二位前缀和求取方式,在下面两个坐标要减去
		a[xa][yb+1]--;
		a[xb+1][ya]--;
		a[xb+1][yb+1]++;//前面减了两次
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			a[i][j]=a[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
	}
}

对照版本:

#include
#include
using namespace std;
int b[1005][1005];
int a[1005][1005];
int n,m;//n*n个格子被m个地毯覆盖
int main()
{
	scanf("%d%d",&n,&m);
	int xa,ya,xb,yb;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[i][j]=a[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
		b[xa][ya]++;//从这个点开始改变,因为二位前缀和求取方式,在下面两个坐标要减去
		b[xa][yb+1]--;
		b[xb+1][ya]--;
		b[xb+1][yb+1]++;//前面减了两次
	}
	int sum=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
			sum=b[i][j];
			a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+sum;
			//a[i][j]表示前缀和		
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cout<<a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]<<" ";//本题中就是自己那个数,别的题可能是某个区间范围
		}
		cout<<endl;
	}
}

线段树

全部修改完再求和,用前缀和与差分。如果是边修改边求取,用线段树
讲解+模板指路
线段树模板

你可能感兴趣的:(洛谷做题笔记(更新ing)