高考完了,耍了快一个月,开始边耍边学习😂
一年多没碰OI了,本来想刷点题回忆下
然鹅我的记忆力不允许
高精加
#include<bits/stdc++.h>
using namespace std;
const int N=600;
char ch[N];
int a[N],b[N],n,m,c[N],x;
int main(){
// freopen("1.in","r",stdin);
cin>>ch;
n=strlen(ch);
for(int i=0;i<n;++i)
a[n-i]=ch[i]-48;
cin>>ch;
m=strlen(ch);
for(int i=0;i<m;++i)
b[m-i]=ch[i]-48;
if(n<m) n=m;
x=0;
for(int i=1;i<=n;++i){
c[i]=(a[i]+b[i]+x)%10;
x=(a[i]+b[i]+x)/10;
}
if(x>0) ++n, c[n]=x;
for(int i=n;i>0;--i)
printf("%d",c[i]);
return 0;
}
高精乘
被坑了,注意判零(见32行)
#include<bits/stdc++.h>
using namespace std;
const int N=3000;
int na,nb,n,a[N],b[N],c[N];
char ch[N];
int main(){
freopen("1.in","r",stdin);
cin>>ch;
na=strlen(ch);
for(int i=0;i<na;++i)
a[na-i-1]=ch[i]-48;
cin>>ch;
nb=strlen(ch);
for(int i=0;i<nb;++i)
b[nb-i-1]=ch[i]-48;
for(int i=0;i<na;++i)
for(int j=0;j<nb;++j)
c[i+j]+=a[i]*b[j];
for(int i=0;i<na+nb;++i){
c[i+1]+=c[i]/10;
c[i]%=10;
}
n=na+nb-1;
while(c[n]==0 && n>0) --n;
while(n>=0) printf("%d",c[n]),--n;
// debug
// printf("\n");
// for(int i=0;i<na+nb;++i) printf("%d ",c[i]);
return 0;
}
并查集
徒手写的时候没加merge
是不可以的:强制father[x]=y
认亲可能会把x从原有的并查集里撕出来
多进行几个这种操作整张图就裂开了
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch) && ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=1e4+5;
int n,m,father[N];
int getfather(int x){return father[x]==x?father[x]:father[x]=getfather(father[x]);}
void merge(int x,int y){
x=getfather(x), y=getfather(y);
if(x==y) return;
father[x]=y;
return;
}
int main(){
// freopen("1.in","r",stdin);
n=in,m=in;
for(int i=1;i<=n;++i) father[i]=i; //?
while(m--){
int z=in,x=in,y=in;
if(z==1) merge(x,y);
else{
if(getfather(x)==getfather(y)) printf("Y\n");
else printf("N\n");
}
}
return 0;
}
转念一想不能强制father[x]=y
认亲可以让他们的爸爸认亲啊
所以去掉merge函数,让原先merge(x,y)
的地方变成father[getfather(x)]=getfather(y)
也是对的
二分答案
二分答案就是这样,对也对的离谱,错也错的离谱
明明思路没一点问题,结果还WA9个点
传送门
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
int in{
int i=0,f=1; char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') f=-1, ch=getchar();
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=1e6+10;
int n,m,a[N],ans,l,r,mid;
signed main(){
// freopen("1.in","r",stdin);
n=in,m=in; l=1;
for(int i=1;i<=n;++i) a[i]=in, r=max(r,a[i]);
while(l<r){
mid=(l+r+1)>>1;
int sum=0;
for(int i=1;i<=n;++i)
if(a[i]>mid) sum+=a[i]-mid;
if(sum>=m) l=(ans=mid);
else r=mid-1;
// printf("%d %d\n",l,r);
}
printf("%lld\n",ans);
return 0;
}
顺带一提,分数规划也用二分法做,要推柿子
分数规划经典题是算性价比,
n
n
n个物品性能
a
i
a_i
ai,价格
b
i
b_i
bi
最大化
∑
a
i
∑
b
i
\frac{\sum a_i}{\sum b_i}
∑bi∑ai
推柿子:
∑ a i ∑ b i > m i d ∑ a i − m i d ∑ b i > 0 \frac{\sum a_i}{\sum b_i}> mid\\ \sum a_i -mid\sum b_i>0 ∑bi∑ai>mid∑ai−mid∑bi>0
一个 m i d mid mid能使得上式成立则该 m i d mid mid合法
树状数组
复健,一打就会
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1; char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') f=-1, ch=getchar();
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=1e6+10;
int n,m,t[N];
int lowbit(int i){return i&-i;}
void Add(int p,int x){
while(p<=n){
t[p]+=x;
p+=lowbit(p);
}
}
int Query(int p){
int sum=0;
while(p>0){
sum+=t[p];
p-=lowbit(p);
}
return sum;
}
int main(){
// freopen("1.in","r",stdin);
n=in,m=in;
for(int i=1;i<=n;++i) Add(i,in);
for(int i=1;i<=m;++i){
int opt=in, a=in, b=in;
if(opt==1) Add(a,b);
else printf("%d\n",Query(b)-Query(a-1));
}
return 0;
}
另外一题
区间加?看到区间第一反应是前缀和,想半天发现肯定不行啊
一看以前的记录,麻了是差分😂想反了
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1; char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') f=-1, ch=getchar();
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=1e6+10;
int n,m,t[N],a[N];
int lowbit(int i){return i&-i;}
void Add(int p,int x){
while(p<=n){
t[p]+=x;
p+=lowbit(p);
}
}
int Query(int p){
int sum=0;
while(p>0){
sum+=t[p];
p-=lowbit(p);
}
return sum;
}
int main(){
// freopen("1.in","r",stdin);
n=in,m=in;
for(int i=1;i<=n;++i) Add(i,(a[i]=in)-a[i-1]);
for(int i=1;i<=m;++i){
int opt=in;
if(opt==1){
int l=in,r=in,x=in;
Add(l,x), Add(r+1,-x);
}else{
int p=in;
printf("%d\n",Query(p));
}
}
return 0;
}
线段树
完蛋,数组大小要*4都搞忘了
证明如下:
n
n
n个数据,显然线段树层数为
⌈
log
2
n
⌉
\lceil \log_2n\rceil
⌈log2n⌉,故所需空间:
∑
i
=
1
⌈
log
2
n
⌉
2
i
=
2
×
2
⌈
log
2
n
⌉
−
1
≤
2
×
2
log
2
n
+
1
−
1
≤
4
n
\sum_{i=1}^{\lceil \log_2n\rceil}2^i=2\times 2^{\lceil \log_2n\rceil}-1\leq 2\times 2^{ \log_2n +1}-1 \leq 4n
i=1∑⌈log2n⌉2i=2×2⌈log2n⌉−1≤2×2log2n+1−1≤4n
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
int in{
int i=0,f=1; char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') f=-1, ch=getchar();
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=1e5+5;
int n,m,tre[N<<2],a[N],laz[N<<2];
void push_up(int p){tre[p]=tre[p<<1]+tre[p<<1|1];}
void push_down(int l,int r,int p){
if(!laz[p]) return;
int mid=l+r>>1;
laz[p<<1]+=laz[p];
laz[p<<1|1]+=laz[p];
tre[p<<1]+=laz[p]*(mid-l+1);
tre[p<<1|1]+=laz[p]*(r-mid);
laz[p]=0; return;
}
void build(int l,int r,int p){
if(l==r) tre[p]=a[l];
else{
int mid=l+r>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
push_up(p);
} return;
}
void update(int L,int R,int val,int l,int r,int p){
if(L<=l&&r<=R){
tre[p]+=val*(r-l+1);
laz[p]+=val;
}else{
push_down(l,r,p);
int mid=l+r>>1;
if(mid>=L) update(L,R,val,l,mid,p<<1);
if(mid<R) update(L,R,val,mid+1,r,p<<1|1);
push_up(p);
} return;
}
int query(int L,int R,int l,int r,int p){
if(L<=l&&r<=R) return tre[p];
push_down(l,r,p);
int mid=l+r>>1, res=0;
if(mid>=L) res+=query(L,R,l,mid,p<<1);
if(mid<R) res+=query(L,R,mid+1,r,p<<1|1);
return res;
}
signed main(){
// freopen("1.in","r",stdin);
n=in,m=in;
for(int i=1;i<=n;++i) a[i]=in;
build(1,n,1);
for(int i=1;i<=m;++i){
int opt=in;
if(opt==1){
int l=in,r=in,k=in;
update(l,r,k,1,n,1);
}else{
int l=in,r=in;
printf("%lld\n",query(l,r,1,n,1));
}
}
return 0;
}