IO方式
-
标准输入输出,scanf,printf
scanf 识别给定字符类型,忽略空格换行,识别末位条件:scanf()!=EOF ;printf最后输出注意**\n** 来换行;数据范围:
int:-109-109
long:-1018-1018
float 32 +/- 3.40282e+038 %f、
double 64 +/- 1.79769e+308 %lf、
scanf printf double %lf %f int %d - float char - %f %c -
头文件为#include<bits/stdc++.h>万能格式;
-
*isupper()/islower()*判断大小写
直接使用‘a’,’z‘判断
-
string s; while(getline(cin,s)){ stringstream ss(s); int sum=0,x; while(ss>>x){ sum+=x; }getline获取一行字符,到达文件末尾退出;
stringtream&int/char/string搭配使用用于字符类型转换;ss自动带有指针,当识别完成后返回false ;
ss每次使用后第二次使用时:ss.clear()清除上一次内容才可以
-
error 时钟角度计算,时针不是标准旨在整点上而是会有分数的部分;
-
**vector(size,value)**size为大小,v为初始值;
-
对于输入一行的问题,采用先getchar吞字符,然后getline(cin,s);
-
字符读入
int init() {//读取一个整数并带有符号 int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; }
9.按照某种格式读取
scanf("%d'%d%c", &mm, &ss, &cc);//2‘55’‘时间
10:printf(“%02d”,mon);

11.时间格式
固定转化:
涉及到年月日时分秒,全部转化成统一单位s、min处理;
如果有跨天,最后加上一个周期。
读入格式有限制,使用规定格式读取:
scanf("%d:%d-%d:%d",&h1,&m1,&h2,&m2);
printf("%02d:%02d-%02d:%02d\n",h1,m1,h2,m2);
sscanf(s,"%d(%d)",x,y);//从字符中格式化读取
while (scanf("%d%d%d",&x,&y,&C)!=EOF){
12.结构体+比较函数+map读取+vector排序
struct Node {
int num;
int count;
};
bool compare(Node n1, Node n2) {
if (n1.count != n2.count) {
return n1.count > n2.count;
}
return n1.num < n2.num;
}
int main() {
int n;
cin >> n;
map<int, int> dict;
vector<Node> list;
for (int i = 0; i < n; i++) {
int tmp;
cin >> tmp;
dict[tmp]++;
}
map<int, int>::iterator it = dict.begin();
for (it; it != dict.end(); it++) {
list.push_back({it->first, it->second});
}
sort(list.begin(), list.end(), compare);
for (int i = 0; i < list.size(); i++) {
Node node = list[i];
cout << node.num << " " << node.count;
if (i != list.size() - 1) {
cout << endl;
}
}
return 0;
}
13.输入字符串,读取其中的数字;将数字转化成字符
string s;
cin>>s;
int num = 0, count = 1;
for(int i = 0; i < s.size()-1; i++){
if(s[i] >= '0' && s[i] <= '9'){
num += (s[i] - '0') * count++;
}
}
//
num = num%11;
char ans;
if(num == 10) ans = 'X';
else ans = num + '0';
俄罗斯方块模拟题
#include<iostream>
using namespace std;
const int N = 15, M = 10, R = 4;
int map[N + 1][M + 1]; //15 x 10的地图
int point[N + 1][2]; //方案里面存在的点
int cnt, x, flag = 1;
int main()
{<!-- -->
for (int i = 1; i <= N; i++) //读入地图
for (int j = 1; j <= M; j++)
cin >> map[i][j];
for (int i = 1; i <= R; i++)
for (int j = 1; j <= R; j++)
{<!-- -->
cin >> x;
if (x) //把图案里面存在的点存起来
{<!-- -->
point[cnt][0] = i;
point[cnt++][1] = j;
}
}
cin >> x;
for (int i = 0; i < cnt; i++)
{<!-- -->
point[i][1] = point[i][1] + x - 1; //把所有点的位置更新
}
while (flag == 1)
{<!-- -->
for (int i = 0; i < cnt; i++) //先给所有的点位置进行移动
point[i][0] += 1;
for (int i = 0; i < cnt; i++)
if (map[point[i][0]][point[i][1]] || point[i][0] > 15) //如果地图上对应点有障碍,或者越界了flag = 0
flag = 0;
if (!flag) //如果flag等于0,那么当前的点都不合法,我们需要退一步
for (int i = 0; i < cnt; i++)
{<!-- -->
point[i][0] -= 1;
map[point[i][0]][point[i][1]] = 1; //这里把合法的点在地图上标记,然后退出循环
}
}
for (int i = 1; i <= N; i++) //输出
{<!-- -->
for (int j = 1; j <= M; j++)
if (j == 1) cout << map[i][j];
else cout << " " << map[i][j];
cout << endl;
}
}
易错小点程序书写规范
- 开数组大数组开在全局变量
- 使用min,max,abs,fabs来减少代码量
- 初始化定义
- memset初始化只用于0,-1;其他使用fill函数。
- 使用vector注意是否为空,空不能访问
- 使用数组检查下标是否合法,-1,操作格外注意 。
CSP骗分

STL
-
pair<>,数对,字典序比较大小
-
vector,数组扩充每次*2,复杂度为O(1)
- v.emplace(x.begin()+1,1)在第二个位置直接插入1,不复制构造
循环写法
for(vector::iterator it = ~~)
- for(int i:v) {}
- for(auto i:v)//只变化
- for(auto& i:v)
-
list链表,reverse_iterator,
for(auto& i:l)
li.reverse()
去重li.sort(),li。unique()
删除全部li。remove
-
string,substr(2,3)//public–bli;
s.c_str()返回常量字符数组
-
stack《》栈
-
大根堆 priority_queue<int,greater > /less
默认为最大堆;定义<右边的边的优先级更高,可以对结构体定义。
-
复杂结构体大小比较:
struct P{
int x,y,z;
bool operator<(const P &p)const{
if(x!=p.x) return x<p.x;
if(y!=p.y) return y<p.y;
return z<p.z;
}
}
-
sort
-
vector v={1,2,3}
//传起始,结束指针、迭代器,返回找到位置迭代器
cout<<*lower_bound(a,a+6,4)/*upperbound(a,a+6,3),位置
*min_element()a,a+10),最小值位置
reverse(a+2,a+6)
排列 next_permutation()
p19
-
algorithm:
匿名函数可以使用;
lower_bound(data.begin(), data.end(), i)

搜索
bfs
vis,dis,queue


迷宫问题bfs:


直接进行一次BFS,把起始的所有点都一次性添加进队列中,dis
初值赋为0(其余点的dis值为inf),最终得到的dis值就是到该点的最小距离。更好的理解方式:加一个超级源点 -> 到各起始点的代价是 0
dfs
选这个数,不选这个数;

再卖菜暴力:





嵌入式
贪心二分***
- 背包问题:价值最大,重量限制:
单位重量价值挑选;
-
最轻的人选择能与它一起坐船的最重的
-
剩下的人越轻越好
-
每步选取最优
-
证明:
反证法:如果交换方案中任意两个元素/相邻的两个元素后,答案不会变得更好,那么可以推定目前的解已经是最优解了。
归纳法:先算得出边界情况(例如 n=1 )的最优解F
1 ,然后再证明:对于每个Fn+1 , 都可以由 Fn 推导出结果。 -
题型 :最常见的贪心题型有两种。
1.我们将 XXX 按照某某顺序排序,然后按某种顺序(例如从小到大)选择。–离线的,先处理后选择
2.我们每次都取 XXX 中最大/小的东西,并更新 XXX。–在线的,边处理边选择。 -
解法:排序解法
用排序法常见的情况是输入一个包含几个权值的数组,通过排序然后遍历模拟计算的方法求出最优值。
后悔解法
思路是无论当前的选项是否最优都接受,然后进行比较,如果选择之后不是最优了,则反悔,舍弃掉这个选项;否则,正式接受。如此往复。 -
区间调度问题:选择 —— 最早的完成时间 Fi 或者最晚的开始时间Si
证明:
从左向右选,一开始的范围是(-∞,+∞),每选择一个区间后就变成了
[Fi ,+∞),让这个范围尽可能越大越好,所以每次要选结束时间最早的,
这样选完之后剩下的范围最大
-
工作调度 先假设每一项工作都做,将各项工作按截止时间排序后入队;
• 在判断第 i 项工作做与不做时,若其截至时间符合条件,则将其与队中报酬最小的元素比较,若第 i 项工作报酬较高(后悔),则 ans += a[ i ].p - q.top () 。用优先队列(小根堆)来维护队首元素最小。struct Node{ 10 ll d,p; 11 }node[maxn]; 12 priority_queue<ll,vector<ll>,greater<ll> >q; 13 bool cmp(Node a,Node b){ 14 return a.d<b.d; 15 } 16 int main(){ 17 cin>>n; 18 for(ll i=1;i<=n;i++) cin>>node[i].d>>node[i].p; 19 sort(node+1,node+n+1,cmp); 20 for(ll i=1;i<=n;i++){ 21 if(node[i].d>q.size()){ 22 ans+=node[i].p;q.push(node[i].p); 23 } 24 else{ 25 if(node[i].p>q.top()){ 26 ans+=node[i].p-q.top();q.pop();q.push(node[i].p); 27 } 28 } 29 } 30 cout<<ans<<endl; 31 return 0; 32 } -
国王游戏


二分 :
int find(int k){
int l=1,r=n,mid;
while(l<r){
mid=(l+r)/2;
if(a[mid]>=k) r=mid;
else l=mid+1;
}
return l;
}
//float
double find(){
double l=1,r=10;
while(abs(r-l)>eps){
double mid=(l+r)/2;
if(f(mid)<=0){
l=mid;
}else{
r=mid;
}
}
return l;
}
- 二分答案 看例题系统上的DC题https://oj.qd.sdu.edu.cn/contest/33/problem/3 https://oj.qd.sdu.edu.cn/contest/33/problem/4
二分答案
最大段最小

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[100002];
int n,m;
int check(int x){
int sum=0,t=0;
for(int i=0;i<n;i++){
if(sum+a[i]<=x) sum+=a[i];
else {sum=a[i]; t++;}
}
return t;
}
int main(){
cin>>n>>m;
int l=-1;
int r=0;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
if(a[i]>l) l=a[i];
r+=a[i];
}
int mid=(l+r)/2;
while(l<r){
if(check(mid)<m) r=mid;
else l=mid+1;
mid=(l+r)/2;
}
cout<<l;
}
平均值最大
二分两种类型
auto lower = std::lower_bound(data.begin(), data.end(), i);
返回指向范围 [first, last) 中首个不小于(即大于或等于) value 的元素的迭代器,或若找不到这种元素则返回 last 。
最小值最大问题–返回右边界需要取到右边界:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JcxQjSSb-1626147807075)(…/…/…/pictures-md/image-20210421200041657.png)]
最大值最小问题–返回需要能取到左边界:

H4贪心二分实验OJ题目分析
-
按照右端点排序,从右向左遍历 贪心[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-igoeiIlt-1626147807077)(…/…/…/pictures-md/image-20210408212313195.png)]
贪心选择标准按照r排序,每一个从右往左遍历标记;mlogm+m(r-l)+n O(n)
sort复杂度为nlogn
- 排序标准

- 贪心选择过程

流水作业调度Johnson法则
- Johnson法则 n+a1loga1+a2+loga2



两个时间根据累计的原则画图得出:

3.数分成几段连续的一段数重量和最大的尽可能小二分答案
- 二分答案 最大重量为l,总重量为r,对于mid若使用mid进行分段使每一段不大于它,分段数大于则重量和应该增加,右侧;反之左侧;最后二分选出一个;nlogn;
通用写法选择左侧:右侧可能为答案;

while(l<r){
int mid =(l+r)>>1;
if(满足条件){
l=mid+1;
}
else{
r=mid;
}
}
return l;
具体对条件处理时注意边界;
二分答案,前缀和数组;数据处理每个数减去平均数;
nlogn

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-II3xbEby-1626147807086)(https://gitee.com/mth01/imgs_md/raw/master/imgs/20210713112208.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kN5BPicp-1626147807087)(…/…/…/pictures-md/20190212185049826.png)]

数学基础
取模运算




- 快速幂

qpow()
ll qpow(int a,int p){
int re=1;
while(p){
if(p&1) re=re*p%mod;
a=a*a;
p>>1;
}
return re;
}
前缀和13
•前缀和作用•
O(1) 求出一个区域所有元素数值之和
•当涉及快速求取某一区域和时,可考虑使用前缀和进行快速计算
•即前缀和通常用于优化算法中的某一步骤,进而降低复杂度
•一维前缀和•sum[i] = sum[i-1] + a[i]•sum[L, R] = sum[R] –sum[L-1]

- 差分

- 尺取法




- 单调栈


- 单调队列

h5数学运算课后练习

1.二维前缀和判断 O(n)=nx26x2
判断一个期间有没有直接在这个区间每一个字符对应的相减可以得到;
存取:
for(int i=1;i<=n;i++){
for(int j=0;j<26;j++){
a[i][j]=a[i-1][j];
}
a[i][s[i]-'A']+=1;
}
检验:
if(a[r][i]-a[l-1][i]==0){
flag=false;
break;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J4LD6fTV-1626147807107)(…/…/…/pictures-md/image-20210407135612869.png)]
2.尺取法 26xn
满足条件,左边右移,不满足,右边右移增加;
模板:
for(l,r;l+25<=n&&r<=n;){
bool flag=check();
if(!flag){
r++;
a[s[r]-'A']++;
}
if(flag){
re=min(re,r-l+1);
a[s[l]-'A']--;
l++;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5pdfDjXa-1626147807108)(…/…/…/pictures-md/image-20210407200038342.png)]
3.单调队列 nlogn
单调队列通常维护 局部 的单调性
前面伪代码!!
求最小值:

最大:

4.直方图求最大矩形面积
- 单调栈 nlogx
从左到右单调栈,右边第一个比它小的
从右到左递增栈,左边第一个比他小的
初始化ll 为0;rr为n+1;

5.差分数组+快速幂nlogn
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kMZd2UXv-1626147807114)(…/…/…/pictures-md/image-20210408142721992.png)]
- 差分数组,快速幂nlogn
- 质数x幂次(差分表示),最后前缀和求出每个质数的最小幂次,求快速幂;
取模运算:模板(上面快速幂模板)

void isprime(int l,int r,int c,int b){//指数ll差分数组
for(int i=2;i<100;i++){
if(c==1) break;
ll cnt=0;
while(c%i==0){
c/=i;
cnt++;
}
cha[i][l]+=cnt*b;
cha[i][r+1]-=cnt*b;
}
}
ll qpow(ll a,ll b,ll m){
if(b==0) return 1;
ll now=qpow(a,b/2,mod);
if(b&1) return a*now%m*now%m;
else return now*now%m;
}
int main(){
int m;
cin>>n>>m;
int l,r,c,b;
while (m--)
{
cin>>l>>r>>c>>b;
isprime(l,r,c,b);
}
ll ans=1;
for(int i=2;i<100;i++){
ll temp=0,mi=(ll)1e14;
for(int j=1;j<=n;j++){
temp+=cha[i][j];
mi=min(temp,mi);
}
ans=(ans*qpow(i,mi,mod))%mod;
}
cout<<ans;
}
h6图
并查集

数组记录应该输出的位置;

分析:并查集的个数-1;求并查集的个数;一开始有n个并查集,之后每次添加一条边,若两个点属于一个并查集,不改变并查集个数;否则,并查集的个数-1,需要修建的条数-1,合并两个并查集;并查集查找过程中修改来减少之后的查找次数;“O(4)”
-
//通过par设置根元素 int find(int x){ int root=par[x];//并查集代表元素 while(root!=par[root]){//找到 root=par[root]; } while(x!=root){//将到达根的所有的par设为根 int t=par[x]; par[x]=root; x=t; } return root; } //合并 void unite(int x,int y){ x=find(x);//找到根之后合并 y=find(y); par[x]=y; }
-
z最远的肯定为叶子节点;树的直径问题,从一点开始找到距离最远的点;然后从最远的点开始找到另一个最远的点;从这两个点开始遍历记录到所有点的距离,每个点上输出两次dfs的最大值;

设立超级原点,到每个点边的权值为c,则为求最小连通子图;
最小生成树:
Kruskal 每次贪心地尝试将图中最小的非树边标记为树边,非法则跳过


并查集复杂度O(mlog(1+m/n**n)

- 画图分析,两个集合不同前进,直到一个为空;


负权环路
-
bellmanford算法,经过m-1次松弛一定会有最短路,每次对每一条边对应的点进行松弛;最后,如果还可以进行松弛说明含有负权环路。
-
Floyd 算法解决的是多源最短路径问题,
对于单源最短路径问题有些许小题大做
• Dijkstra 算法在图中存在负权边时不能保证结果的正确性
算法模板****:**
Bellman-ford 算法及其队列优化 (SPFA)可以给出源点 S 至图内其他所有点的最短路𝑂(𝑛𝑚);
所有的点都在最短边上;
spfa就是有队列优化的bellmanford算法;求解有负数的单元最短路径问题。所有的都可以用spfa
- 负权环路
当图中存在负环时,那么可以沿着负环不断走下去,那么最短路长
度为负无穷,没有意义。如果图中含有负权边时,则需要考虑图中最短路是否存在。也就是
我们应用 Bellman - ford 算法及其队列优化所求出的最短路是否有效。
- 负权环路

对于本题负权环可以对任意一点,所以从任意一点开始进行算法判断是否存在负权环路即可;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WWw0Qu1i-1626147807139)(…/…/…/pictures-md/image-20210422222925178.png)]
- dij算法单元最短路径求出去的最短路,然后反向建图,单元最短路求出回去的最短路,两者相加;复杂度 𝑂 ( 𝑛 + 𝑚) 𝑙𝑜𝑔𝑛 )


二分答案,+单元最短路径dij;二分最小值最大,为最大问题,mid=(l+r+1)/2;
n+m)lognlogm


H8

manacher算法,只对没有检查过的部分进行遍历,复杂度为O(n);
特殊的len数组,回文串的长度为leni-1?
len性质决定在处理过的字符串中,回文长度为2*leni-1,期中其他符号#比原本符号多一个,所以本来符号位leni-1;
int malacher(string s){
//预处理
str[0]='@';//防止越界首部增加一个
str[1]='#';//#a#b#
for(int i=0;i<n;i++){
str[(i+1)*2]=s[i];//error str[i]
str[(i+1)*2+1]='#';
}
str[2*n+2]='?';
memset(len,0,sizeof len);
int po=0,mx=0,ans=0;//已有的回文串最右端到达mx,对应的中心位置为po
for(int i=1;i<=n*2+1;i++){
if(mx>i) len[i]=min(mx-i,len[2*po-i]);//在mx左边,对称位置的长度,或者超过当前限制
else len[i]=1;//在mx右边
while (check(str[i-len[i]],str[i+len[i]]))//以i位置为中心左右两边的是否回文更新+1
{
len[i]++;
}
if(i+len[i]>mx){//若最右回文串位置需要更新
mx=i+len[i];po=i;
}
ans=max(ans,len[i]);//取最大回文串长度
}
return ans-1;//可以证明回文串长度为len数值-1;
}

拓扑排序
;入度为0
建图,通过入度为0的队列存储排列,保证学号小的排在前面通过一个优先队列实现;

kahn算法实现拓扑排序:

n*nlogn;
差分问题:转化为图

i-j<=w:j到i权威w;
i-j<w==>i-j<=w-1;
i-j>w==>j-i<-w;
i-j=w==>i-j>=w&&j-I<=w;
建立前缀和数组,后一个与前一个的图限制,必须权重为0或者1,;
从0开始到最后spfa遍历,小于等于转化为最小路问题,求最多为多少,为求图的最短路问题,使用spfa,从0求单元最短路径有负边;
差分建图:
cin >> n >> m;
dis[0] = 0;
memset(head, 0, sizeof(head));
for (int i = 0; i < n; i++)
{
add(i, i + 1, 1);
add(i + 1, i, 0);
}
int k, a, b, c;
while (m--)
{
cin >> k >> a >> b >> c;
a-=1;
switch (k)
{
case 1:
add(a, b, c);
break;
case 2:
add(b, a, -c);
break;
case 3:
add(a, b, c - 1);//输入error
break;
case 4:
add(b, a, -c - 1);
break;
default:
add(a, b, c);
add(b, a, -c);
break;
}
}
spfa:nlogn
void spfa(int x)
{
for (int i = 0; i <= n; i++)
{
dis[i] = inf;
vis[i] = 0; cnt[i]=0;
}
queue<int> q;
dis[x] = 0;
vis[x] = 1;//防止已经入队的重复入队。
q.push(x);
bool flag=false;
while (!q.empty())
{
int now = q.front();
q.pop();
vis[now] = 0;
for (int e = head[now]; e; e = nxt[e])
{
if (dis[v[e]] > dis[now] + w[e])
{
if(v[e]==n) flag=true;//n处更新过了表示有最短路,
//否则负权环路可能没有最短路不能图不连通
dis[v[e]] = dis[now] + w[e];
cnt[v[e]]=cnt[now]+1;//求出最短路最多遍历n-1次,若超过说明没有最短路
if(cnt[v[e]]>=n){
cout << "impossible";
return;
}
if (!vis[v[e]])//没在队列中,后面可能会有最短路,入队
{
q.push(v[e]);
vis[v[e]] = 1;
}
}
}
}
if (flag)
cout << dis[n];
else
cout << "impossible";
}
- 拓扑排序将入度为0的点放入,每次减去与点相连的边

强联通分量

强联通分量表明分量内部互相通信,进行缩点,求入度为0的点,就是要求的最少人数。
强联通分量求法kasaraju算法:
一遍dfs求出后序序列,按照逆后序序列求出标记连通分量,每个点都有连通分量序号,然后对所有点,若不属于一个连通分量,可以对出点所在的连通分量增加一个入度,最后求出根据连通分量建图中的所有入度为0的点。


缩点直接求新图的入度数。
h9模拟题方程式,文件系统
方程式问题:
使用split分解,使用=分解左右两边,对于左右两边分别使用+分解成每一个化学式,,对每个化学式执行读取系数,读取元素,遇到()时进入一个新的读取范围的操作,对于新构件的化学式结构体,存取元素和个数用map实现;
关键在于如何设计每个函数的返回值与参数;
#include<bits/stdc++.h>
using namespace std;
struct elementset{
map<string,int> p;
void operator *=(const int n){
for(auto& x:p){
x.second*=n;
}
}
void operator +=(elementset e){
for(auto& x:e.p){
p[x.first]+=x.second;
}
}
bool operator ==(elementset e){
return p==e.p;
}
};
vector<string> split(string& s,char p){
stringstream ss(s);
string temp;
vector<string> res;
while (getline(ss,temp,p))
{
res.push_back(temp);
}
return res;
}
int read_int(string& s,int& p){
int res=0;
while (p<s.length()&&isdigit(s[p]))
{
res=res*10+s[p]-'0';
p++;
}
return res==0?1:res;
}
string read_element(string& s,int& p){
string res;
res+=s[p];
if(islower(s[p+1])) res+=s[++p];
return res;
}
elementset str2set(string& s,int& p){
int head=read_int(s,p);
elementset res;
while (p<s.size())
{
if(s[p]=='('){//有括号重新设置为新的化学式
res+=str2set(s,++p);
}
else if (s[p]==')')//左括号内部的*,只会对括号里面的进行操作
{
res*=read_int(s,++p);
return res;
}
else{
string temp=read_element(s,p);
int i=read_int(s,++p);
res.p[temp]+=i;
}
}
res*=head;
return res;
}
bool solve(vector<string> v){
elementset e[2];
vector<string> oneside;
for(int i=0;i<2;i++){
oneside=split(v[i],'+');
for(auto& x:oneside){
int p=0;
e[i]+=str2set(x,p);
}
}
return e[0]==e[1];
}
int main(){
freopen("1.in","r",stdin);
int n;
cin>>n;
string s;
while (n--)
{
cin>>s;
vector<string> side2=split(s,'=');
if(solve(side2)) cout<<"Y"<<"\n";
else cout<<"N\n";
}
return 0;
}
文件系统:
创建一个文件结构体,type表示哪种文件,以及相应的各个文件属性;创建文件系统结构体,存取所有的节点并给定相应的编号,可以对文件树进行搜索查找修改等操作。
#include<bits/stdc++.h>
#define ll long long
#define N 4000003
using namespace std;
struct file{
int type;//0 普通文件 1--目录文件
ll size;
ll ld,lr,sld,slr;//ld孩子大小 lr后代大小
map<string,ll> child;
file(int _t=0,int _s=0){
type=_t;
size=_s;
ld=lr=sld=slr=0;
child.clear();
}
void addsize(ll _size,bool flag=false){
if(flag) sld+=_size;
slr+=_size;
}
bool check_size(ll _size,bool flag=false){
if(flag&&ld&&sld+_size>ld) return false;
if(lr&&slr+_size>lr) return false;
return true;
}
bool change_size(ll newld,ll newlr){
if(newld&&sld>newld) return false;
if(newlr&&slr>newlr) return false;
ld=newld;
lr=newlr;
return true;
}
};
struct filesystem{
int root,cnt;//根目录 计数器
string name;
vector<string>path;
file files[N];
filesystem(){
root=1;
cnt=1;
files[1]=file(1);
}
void get_path(string s){
path.clear();
name="";
stringstream ss(s);
string ch;
getline(ss,ch,'/');
while (getline(ss,ch,'/'))
{
path.push_back(ch);
}
if(path.size()) name=ch,path.pop_back();
}
int find(){
int now=root;
if(name=="") return root;
for(auto& x:path){
int t=files[now].child[x];
if(!t) return 0;
//是否冲突
if(files[t].type==0) return -1;//!!目录文件与普通文件冲突
now=t;
}
return files[now].child[name];
}
int get_type(int now){
return files[now].type;
}
ll get_size(int now){
if(files[now].type==0) return files[now].size;
return files[now].slr;
}
bool create_file(ll size){
int now=root;
//是否可以创建文件
for(auto& x:path){
if(!files[now].check_size(size)) return false;
if(files[now].child[x]==0){
//目录缺失创建目录
files[++cnt]=file(1);
files[now].child[x]=cnt;
}
now=files[now].child[x];
}
if(!files[now].check_size(size,true)) return false;
//创建普通文件
now=root;
for(auto& x:path){
files[now].addsize(size);
now=files[now].child[x];
}
files[now].addsize(size,true);
files[++cnt]=file(0,size);
files[now].child[name]=cnt;
return true;
}
bool remove_file(ll size){
int now=root;
for(auto& x:path){
files[now].addsize(-size);
now=files[now].child[x];
}
if(files[files[now].child[name]].type==0){
files[now].addsize(-size,true);
}
else files[now].addsize(-size);
files[now].child.erase(name);
//return true;
}
bool change_size(int now,ll newld,ll newlr){
return files[now].change_size(newld,newlr);
}
}File;//只有在外面时才行??
int main(){
int n;
// filesystem File;
cin>>n;
char c;string s;
while (n--)
{
cin>>c;
cin>>s;
File.get_path(s);
int now=File.find();//0不存在,-1与目录文件冲突,+数存在
if(c=='C'){
ll size;
cin>>size;
if(now==-1){
printf("N\n");
continue;
}
else if(now>0){
if(File.get_type(now)==0)
File.remove_file(File.get_size(now));//普通文件存在
else{
printf("N\n");
continue;
}
}
if(File.create_file(size)) printf("Y\n");
else{
if(now>0) File.create_file(File.get_size(now));//恢复
printf("N\n");//数组中删除只是在父节点里删除指向孩子,实际文件还在
}
}
else if(c=='R'){
if(now>0) File.remove_file(File.get_size(now));
printf("Y\n");
}
else if(c=='Q'){
ll ld,lr;
cin>>ld>>lr;
if(now>0&&File.get_type(now)==1){
if(File.change_size(now,ld,lr)) printf("Y\n");
else printf("N\n");
}
else{
printf("N\n");
continue;
}
}
}
return 0;
}
W7416模拟
模除问题:取模只能减去第一个值


每个只要和他上一次看的不一样就行,保证当天去的看的都不一样;
用一个集合存取已经放过的牌子,保证不会重复放入,对于每一天来的人数,读他们最近的一天,那么要展示的就是最近的前一天的牌子,把它放入到牌子集合中,更新每一个人所看到的最近一天的编号。
#include<bits/stdc++.h>
using namespace std;//20min做出来,考场上先看全部的题,先尽量做,有点小毛病确实看不出来
int k[1000000];//存取一个人上一次看的最近一次的牌子的显示天数
int main(){
int N;
scanf("%d",&N);
for(int i=0;i<1000000;i++){
k[i]=N+1;
}
int num;
int m=N+1,x;
set<int> s;//集合如果已经放过了不会增加,存取放过的牌子
vector<int> temp;//存取一天来看的人的编号
for(int j=1;j<=N;j++)
{
temp.clear();
m=N+1;//几个人看的最小的牌子数
scanf("%d",&num);//多少人
while (num--)
{
scanf("%d",&x);
temp.push_back(x);
if(k[x]<m) m=k[x];//这几个人看的最近的天的编号
}
s.insert(m-1);//这一天应该展示的牌子为m-1,放入集合中
for(int nm:temp){
k[nm]=m-1;//更新这一天来的每一个人所看的最近编号
}
}
printf("%d",s.size());
}
h10
根据k加上不同时间,注意数字匹配;
小中大:

这里int问题深入:https://blog.csdn.net/cressball/article/details/43016307 %d是对存取的数按照整数方式读取,实际存储按照float型,读取错误,应先用int强制转化。
树状数组:
logn从0开始,数据从1开始的特殊处理。

逆序对:

先按照运行时间和内存时间字典序排序,按照运行时间顺序,来逐个将占用放到树状数组中,每次ask就可求出;第一个排序保障了第一层有序,第二个可以求取前缀和;
#define N (int)1e6+10
ll tr[N*2];
int n;
struct point
{
int x;
int y;
}p[N*2];
bool cmp(point& p1,point& p2){
if(p1.x!=p2.x) return p1.x<p2.x;
return p1.y<p2.y;
}
// #define lb(x) (x&(-x))
int lb(int x){
return x&(-x);
}
void change(int x,int v){
for(int i=x;i<=N;i+=lb(i)){//为什么是小于N????
tr[i]+=v;
}
}
ll ask(int x){//logn
ll res=0;
for(int i=x;i>=1;i-=lb(i)){
res+=tr[i];
}
return res;
}
int main(){
memset(tr,0,sizeof(tr));
int cnt=0;
scanf("%d",&n);
int t1,t2;
for(int i=0;i<n;i++)//n
{
scanf("%d%d",&t1,&t2);
p[cnt].x=t1+1;//否则死循环
p[cnt].y=t2+1;
cnt++;
}
sort(p,p+cnt,cmp);
ll re[n+5]{0};
int t;
for(int i=0;i<cnt;i++){//2nlogn
t=p[i].y;
re[ask(t)]++;
change(t,1);
}
for(int i=0;i<n;i++){//n
printf("%lld\n",re[i]);
}
}
- 树状数组从索引1开始,而[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5yuKIqA9-1626147807160)(…/…/…/pictures-md/image-20210518094656380.png)]处理每个都+1就行;如果有0会死循环,tle

线段树:



模板:
建树:
单点修改:

区间查询:


读取单个字符。直到读取到目标:
其他读入,优先考虑cin,getline;
getline(cin,s,‘#’)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define M (int)1e6//范围matters
#define inf 0x7fffffff
int m,p;
ll tmp=0;
ll d[M];//若为2倍 re
char x;ll t;
ll len=0;
ll max(ll a,ll b){
if(a>b) return a;
return b;
}
void upd(int x,int l,int r,int loc,ll v){//x,d的索引,l-r为d表示的范围
//loc更新位置,c更新值
if(l==r){//loc
d[x]=v;
return;
}
int mid=(l+r)/2;
if(loc<=mid) upd(x*2,l,mid,loc,v);
else upd(x*2+1,mid+1,r,loc,v);
d[x]=max(d[x*2],d[x*2+1]);
return;
}
ll ask(int x,int l,int r,int p1,int p2){//p1,p2要查询的位置
//l r所在的节点表示的区间范围
if(l>=p1&&r<=p2) return d[x];
int mid=(l+r)/2;
if(p2<=mid) return ask(x*2,l,mid,p1,p2);
else if(p1>mid) return ask(x*2+1,mid+1,r,p1,p2);
else{
ll lch_val=ask(x*2,l,mid,p1,p2);
ll rch_val=ask(x*2+1,mid+1,r,p1,p2);
return max(lch_val,rch_val);
}
}
int main(){
scanf("%d%d",&m,&p);
for(int i=1;i<=m;i++) d[i]=-inf;//初始-inf很小
for(int i=1;i<=m;i++)
{
// getchar();
// scanf("%c%lld",&x,&t);
x=getchar();//读入error
while (x<'!')
{
x=getchar();
}
scanf("%lld",&t);
if(x=='A'){
t=(t+tmp)%p;
upd(1,1,m,len+1,t);//更新线段树范围始终1-m
len++;//len为位置
}
else{
tmp=ask(1,1,m,len-t+1,len);//最后t个的最值
printf("%lld\n",tmp);//lld输出ll
}
}
return 0;
}
hw11 动态规划
爬台阶



选数不同个数不相邻限制的dp
前缀和数组,方便计算前k个的和。
数的数量不同;

当前选中,前面一个不能选;状态全部选择;
i表示数;cnt记录每个数的数量;
矩阵选数
给3行,罗列状态转移方程;
当前为0,前一个为012罗列,二维;


最长上升子序列


更新的前面的值越小越好,所有找到大于这个数的第一个然后更新;lower_bound二分查找。


最长公共子序列


背包问题
0-1背包:






[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7MNVffLZ-1626147807188)(…/…/…/pictures-md/image-20210611064259535.png)]
完全背包:

滚动数组优化:

//0-1背包 重量数据大nw过大
void solve(){
for(int i=0;i<n;i++){
for(int j=V;j>=v[i];j--){
dp[j]=min(dp[j],dp[j-v[i]]+w[i]);
}
}
}
//完全背包
void solve(){
for(int i=0;i<n;i++){
for(int j=w[i];j<=W;j++){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
}
多重背包:



[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jq4v5nAx-1626147807195)(…/…/…/pictures-md/image-20210611065829969.png)]

分组背包:




超大背包:




lower找到容量小于v-sw的最大价值,对应拿当前组合物品;
hw13区间DP状压DP
石子合并区间dp



环状拆环为连:

括号列匹配:
枚举区间长度,确定左边界,右边界自然确定







方格填充:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D6c2Ssji-1626147807213)(…/…/…/pictures-md/image-20210611072835389.png)]


判断是否连续偶数个0:

hw14 树状dp
最长直径
最大直径问题:



t5城市规划

重要的节点中间选取:

求每一条边的代价;




最大区间和:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d6HvaPC6-1626147807223)(…/…/…/pictures-md/image-20210611081137687.png)]



维持一个单调队列保证后来的更小。

f[i][j]表示第i次停在j的方案数;倒推 边界为f[k+1][j]=1(j!=b)
hw15矩阵快速幂
基本结构体;
快速幂:

矩阵快速幂:

线性递推式转化成转移矩阵;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VN1Mz8HH-1626147807230)(…/…/…/pictures-md/image-20210617221635652.png)]


推导矩阵表达,最后根据终止的条件时候初始值给出结果。矩阵过剩没有问题。



矩阵快速幂转化方法:dp转移复杂度过高时使用;
转化成矩阵形式套用模板。

只需要将*运算做修改。















被折叠的 条评论
为什么被折叠?



