目录
ios::sync_with_stdio(false);
对sort 函数制定排序规则
例题
给出n个区间,判断时候有哪个区间是被包含在哪个区间的 ,如果有多个输出其中的一组。先输出被包含的区间(他的次叙数),再输出包含他的区间的次序输,如果没有输出的就输出-1 -1
思路
先按左边界从小到大排序,左边界相同的就再按右边界从大到小排序,如果再相同就按序号从小到大排序,可以给sort函数制定一种排序规则,这题我觉得我得背下来,既有结构体还有对sort函数制定排序规则的应用,排序完了,就判断如果一个点的下个点的右边界小于等于他就是包含于他的,输出就行了
代码
bool paixu(Node x,Node y)
{
if(x.l!=y.l)
return x.l<y.l;
else
{
if(x.r!=y.r)
return x.r>y.r;
else
return x.id<y.id;
}
}//给出一种排序规则用在sort函数里
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].l>>a[i].r;
a[i].id=i;
}
int f=0;
int j;
sort(a+1,a+1+n,paixu);//可以给sort制定排序规则 学到了
for(int i=2;i<=n;i++)
{
if(a[i].r<=a[i-1].r)
{
f=1;
j=i;
break;
}
}
if(f)
cout<<a[j].id<<" "<<a[j-1].id<<endl;
else
cout<<"-1 -1"<<endl;
return 0;
}
解释
上面制定的规则就是,先按r从小到大排序,如果r相同就按l从大到小排,如果l再一样就按id从小到大排
前缀和与差分
前缀和与差分
a1,a2,a3。。。an
bi=ai-a【i-1】,b1=a1
那么ai=b1+b2+b3+…+bi,
证明= a1+a2-a1+a3-a2+…+ai-ai-1=ai
bi就是ai的差分 ,ai就是bi的前缀和
求前缀和
for(int i=1;i<=n;i++)
s[i]=s[i-1]+a[i];
求差分
for(int i=1;i<=n;i++)
b[i]=a[i]-a[i-1];
利用差分对区间【l,r】都加上c
给a数组区间【l,r】上面加上一个常数c
b【l】+=c
b【r+1】-=c
原理是ai=b1+。。+bi,如果l之前的都没变加到这个区间里的因为bl加c 了所以al也加c 了出去区间之后减了一个c 抵消掉了
求a,b的最小公倍数
int gcd(int a,int b)
{
if(b==0)return a;return gcd(b,a%b);
a,b的最小公倍数为
a/gcd(a,b)*b
}
快速幂
MOD=10^9+9
ll quick_pow(ll a, ll b) {
if(a == 0)
return 0;
if(b == 0)
return 1;
ll ans = 1;
ll base = a % MOD;
while(b) {
if(b & 1)
ans = (ans * base) % MOD;
base = (base * base) % MOD;
b >>= 1;
}
return ans;
}求x的y次幂
头文件
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<bitset>
#include<ctime>
#include<deque>
#include<stack>
#include<sstream>
0 到n个数顺时针循环了 sum 求到哪了的求法
cout<<(sum%n+n)%n<<endl;
注意sum%n是不对的
将字符串中的数转化为int 类型(stringstream)
str 是一个字符串 假设输入了 数字 怎样转换成int 类型的
sring tream ss;
这种定义类型的头文件是sstream
`
#include<sstream>
stringstream ss;
ss<<str;
int w;
ss>>w;
//这样就把str 的数字转化成整数类型并且放在了w里面
另一种方法
unsigned long long atoi(char *pch,int len)
{
unsigned long long ret=0;
for(int i=0;i<len;i++)
{
if(pch[i]>='0'&&pch[i]<='9')
{
ret*=10;
ret+=(pch[i]-'0');
}
else
{
//此处是出错处理,给的字符中有非数字的字符.
continue;
}
}
return ret;
}
字母加一个数,如果大于z 了就变成a
char a;
a+=sum%26;
if(a>'z')
a-=26;
判断互质||求两个数的最大公约数
bool isrp(int a, int b)
{
if(a==1||b==1) // 两个正整数中,只有其中一个数值为1,两个正整数为互质数
return true;
while(1)
{ // 求出两个正整数的最大公约数
int t = a%b;
if(t == 0)
{
break;
}
else
{
a = b;
b = t;
}
}
if(b>1) return false;// 如果最大公约数大于1,表示两个正整数不互质
else return true; // 如果最大公约数等于1,表示两个正整数互质
}
优先队列
优先队列是具有自动排序功能的队列
priority_queue q(默认的);从大到小排列 的队首最大对应大根堆或者是这样声明priority_queue<int,vector,less >q;
riority_queue <int,vector,greater > q;从小往大排布队首最小对应小根堆
greater队首最小 less队首最大
纸牌传递问题
*单方向传递纸牌问题,前提是纸牌数必须 能整除人数,使所有人纸牌数都相等的最少传递次数为:设纸牌数为a【i】平均数为avr,
次数为a【i】-avr
的前缀和的是s【i】的绝对值再求前缀和。
如果是环形传递纸牌问题,答案是数列a[i]-ave的前缀和s【i】然后排序,
排序完了求中位数的项,abs(s【i】-s【k】)的累加,k为n+1>>1
//例题 acwing 105 七夕祭
离散化处理
/其实本题的思路就在于把会某种语言的数量记下来方法本题重要思想离散化,数据大还没有用就可以把它变成12345…只要不影响判断就行了 离散化的步骤为
int n, a[maxn], t[maxn];
//这里以下标1为序列的起点,一般情况下从0开始也可以
for(int i = 1;i <= n;i++)
{
scanf("%d", &a[i]);
t[i] = a[i];//t是一个临时数组,用来得到离散化的映射关系
}
//下面使用了STL中的sort(排序),unique(去重),lower_bound(查找)函数
sort(t + 1, t + n + 1);//排序
int m = unique(t + 1, t + 1 + n) - t - 1;//去重,并获得去重后的长度m
for(int i = 1;i <= n;i++)
{
a[i] = lower_bound(t + 1, t + 1 + m, a[i]) - t;//通过二分查找,快速地把元素和映射对应起来
}
这样就把大数据全缩*/
//当然就是用数组下标的形式记下来,但是太大的话对空间来说可能会不合适
//例题 acwing 电影103
对类的<运算符重载
struct node { //定义一个结构体node(节点)
int x;
int y;
int len; //node中有3个成员变量x,y,len
bool operator <(const node &a)const {//重载<操作符。可以对两个node使用<操作符进行比较
return len<a.len;
}
}
//这样就把对类排序的根据变成了len 的大小
常用与把类放入优先队列按什么排序
二维数组的前缀和
二维数组的前缀和dp[i][j]表示的是 i.j这个点为右下角 的整个矩形的和
求法为 dp[i][i]+=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]
求 一个二维数组里长为x,宽为y矩形的面积就可以用这个前缀和求 s=dp[i][j]-dp[i-x][j]-dp[i][j-y]+dp[i-x][j-y]
例题见 acwing 99激光炸弹和 山师寒假训练赛1 a题
拓扑排序
定义
我大概可以理解成就是一个有向图,先输出没有被指向的点,他输出了之后他指向的点 的被指向数减1,依次输出没有指向的点
类似这样的序列都是拓扑序列
典型题列
题意
给你n个任务,编号就为(1,2,3,4一直到n),然后再给你一m对关系,i,j表示j完成之前必须要先完成i,让你输出一个可能的完成任务先后的序列
思路
典型的拓扑序列的水题,就先记录后输出的数有几个数指向他,把没有点指向的数放到队列里,然后把队列的队首保存到数组里,删除,再判断这个队首指向的点,如果改点只有一个数指向他(即刚刚删除的队首)就可以把它放到队列,因为他已经没有点指向他了 之前指向他的输出了,如果他有多个点指向他,就把指向他的点数减一,依次进行知道队列空了为止
代码1(已经看懂了)
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=105;
int n,m,son[N],toposort[N],t;
vector<int>G[N];//二维vector的定义
void init()
{
for(int i=1; i<=n; ++i){
G[i].clear();
}
memset(son, 0, sizeof(son));
}
int main()
{
ios::sync_with_stdio(false);//不加cin会超时
int u,v;
while(cin>>n>>m)
{
if(n==0&&m==0)
return 0;
init();
for(int i=1;i<=m;i++)
{
cin>>u>>v;
G[u].push_back(v);
++son[v];//每有一个数指向它 它的头就加1
}
queue<int>q;
for(int i=1;i<=n;i++)
{
if(son[i]==0)
q.push(i);
}//把所有没有指向的 压入队列
int pos=0;
while(!q.empty())
{
int t=q.front();
q.pop();
toposort[pos++]=t;//将队首放进去
for(int v=0;v<G[t].size();v++)
{
if(--son[G[t][v]]==0)
q.push(G[t][v]);//再看队首后面的元素因为已经将t放进去了
//t后面只有一个指向的的元素也就没有元素指向了 就可以把他也放进去,
//有两个指向的就指向点减一
}
}
for(int i=0;i<pos;i++)
{
if(!i)
cout<<toposort[i];
else
cout<<" "<<toposort[i];
}
cout<<endl;
}
return 0;
}
代码 2(写的时候没看明白)
第二种方法是《算法导论》上的, 刘汝佳的《算法入门经典》P 111页上介绍的也是这种方法。
这种方法是用dfs,从根节点开始深搜,每搜到一点记录下来开始搜索和结束搜索的时间戳;
然后根据结束时间从大到小排序即可。这种方法时间效率更高,更推荐。
推荐的解法:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int G[110][110],n,m,a,b;
int vis[110],c[110], topo[110], t;
bool dfs(int u){
vis[u] = -1; //表示正在访问
for(int v=1; v<=n; ++v) if(G[u][v]){
if(vis[v] == -1) return false; // 如果存在有向环,失败退出
else if(!vis[v] && !dfs(v)) return false;
}
// 结束访问
vis[u] = 1; topo[--t] = u;
return true;
}
bool topoSort(){
t = n;
memset(vis, 0, sizeof(vis));
for(int u=1; u<=n; ++u)
if(!vis[u] && !dfs(u)) return false;
return true;
}
int main(){
#ifdef LOCAL
freopen("input.txt","r",stdin);
#endif
// 注意输入那里的结束条件不能是 n&&m,因为m可能是0
while(~scanf("%d %d",&n,&m) && n+m){
memset(G, 0, sizeof(G));
for(int i=0; i<m; ++i){
scanf("%d %d",&a,&b);
G[a][b] = 1;
}
if(topoSort()) {
printf("%d",topo[0]);
for(int i=1; i<n; ++i)
printf(" %d",topo[i]);
printf("\n");
}
}
return 0;
}
判断两个字符串是否含有相同的字母
代码
#include<iostream>
#include<string>
#include<cstring>
int vis[36];
using namespace std;
int main()
{
int t;
cin>>t;
string str1,str2;
while(t--)
{
memset(vis,0,sizeof vis);
cin>>str1;
cin>>str2;
for(int i=0;i<str1.length();i++)
vis[str1[i]-'a']=1;
int f=0;
for(int i=0;i<str2.length();i++)
if(vis[str2[i]-'a']==1)
{
f=1;
break;
}
if(f)
cout<<"YES";
else
cout<<"NO";
cout<<endl;
}
return 0;
}
尺取法
看收藏的笔记里有
然后 牛客 20200204的H题 也是尺取法做的
没明白的题
0204牛客第一题
矩阵快速幂
见收藏的笔记
与1判断奇偶
i&1=1 i为奇数,如果1&1=0 i为偶数
比如说 3 二进制是 11 ,1 二进制是01
与运算时 01 为1
求逆元
逆元的定义
当求解公式:(a/b)%m 时,因b可能会过大,会出现爆精度的情况,所以需变除法为乘法:
设c是b的逆元,则有bc≡1(mod m);
则(a/b)%m = (a/b)1%m = (a/b)bc%m = ac(mod m);
即a/b的模等于ab的逆元的模;
逆元就是这样应用的;
求逆元的方法
费马小定理
在是素数的情况下,对任意整数都有。
如果无法被整除,则有。
可以在为素数的情况下求出一个数的逆元,,即为逆元。
题目中的数据范围1<=x<=10^9,p=1000000007,p是素数;
以x肯定就无法被p整除啊,所以最后就得出x^(p-2)为x的逆元啦。
代码
const int mod = 1000000009;
long long quickpow(long long a, long long b) {
if (b < 0) return 0;
long long ret = 1;
a %= mod;
while(b) {
if (b & 1) ret = (ret * a) % mod;
b >>= 1;
a = (a * a) % mod;
}
return ret;
}
long long inv(long long a) {
return quickpow(a, mod - 2);
}
最大公约数和最小公倍数 欧几里算法
求最大公约数 gcd
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
求最小公倍数 lcm
int lcm(int a,int b){
return a/gcd(a,b)*b;
}
控制循环
加入 26的字母循环 加上一个sum ,首先得 sum变成对26去余数,然后加上sum,ranhou 如果 a[i]-a>25,a[i]-=26;
来源:CSDN
作者:xgx984826498
链接:https://blog.csdn.net/xgx984826498/article/details/103929827
