我又来了!
爆切了一大堆没用的水题后,我终于来发题解啦!!!
T1
原题https://iai.sh.cn/problem/792
我们考虑通过计算最长上升连续自然数串(是这么叫的吗)来获取答案
可以开两个数组 和 来计算
#include<bits/stdc++.h>
using namespace std;
const int N=100007;
int n,a[N],k[N],s[N],ans;//k就是f
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
for(int j=1;j<=n;j++){
k[j]=s[a[j]-1]/*计算最长*/+1;
ans=max(ans,k[j]);
s[a[j]]=max(s[a[j]],k[j]);//加入计算最大值
}
printf("%d\n",n-ans);
}
T2
原题https://iai.sh.cn/problem/804
最有趣的一道
直接暴力是的,代码……自己想去吧
我们这里用的是一种超级简单,超级快速,猛新易上手的方法——单调队列法
众所周知,单调队列有三个变量——存储答案的列表以及头和尾的下标
当翻转时,调换头和尾的下标
当移动时,要分情况
- 头在后面时,把尾部的数调到头部去(++),尾部的数清空
- 头在前面时,把尾部的数调到头部去(--),尾部的数清空
不过为了防止越界,数组要开到!
其实就是牺牲空间换时间
还是挺快的
算是其中最简单的一种方法
细节不多
#include<bits/stdc++.h>
using namespace std;
const int N=500000;
int n,a[3*N+7];
int h=N,t=N;
char s[N+7];
int main(){
scanf("%d\n%s",&n,s+1);
t+=n-1;
for(int i=1,j=h;j<=t;i++,j++)
a[j]=i;
int size=strlen(s+1);
for(int i=1;i<=size;i++){
if(s[i]=='f')
swap(h,t);
else{
if(h>t)
a[++h]=a[t],a[t]=0,++t;
else
a[--h]=a[t],a[t]=0,--t;
}
}
if(h>t)
for(int i=h;i>=t;i--)
printf("%d\n",a[i]);
else
for(int i=h;i<=t;i++)
printf("%d\n",a[i]);
puts("");
}
T3
原题https://iai.sh.cn/problem/811
其实很简单
我们发现,可以每次跑一遍dfs
但绝对超时(想要代码?我只能告诉你优化后还是只有70)
正解方法是:前面出现的余数出现了也是多余的
余数定理告诉我们
如果,则一定有和
那么,前面那个想法的正确性也就不用说明了
#include<bits/stdc++.h>
using namespace std;
#define int __int128
int read(){
int a=0,b=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-')
b=-1;
c=getchar();
}
while(isdigit(c)){
a=a*10+c-'0';
c=getchar();
}
return a*b;
}
void print(int x){
if(!x)
return;
if(x<0)
putchar('-'),x=-x;
print(x/10);
putchar(x%10+'0');
}
int d[10007];
void bfs(int p){
queue<int> q;
q.push(1);
while(!q.empty()){
int now=q.front();
q.pop();
if(now%p==0){
print(now);
putchar('\n');
return;
}
if(d[now%p])
continue;
d[now%p]=1;
now*=10;
q.push(now);
q.push(now+1);
}
}
signed main(){
int q;
q=read();
while(q--){
memset(d,0,sizeof d);
int x;
x=read();
bfs(x);
}
}
T4
原题https://iai.sh.cn/problem/809
默认大家学过LCA了
不懂的戳这->link
思路是树上差分(不会的请自行离开,我实在无语)
如图
1
\
2————4 lca
end /
3 start
如图是一个人的旅行计划(从3出发到2)
首先,start处和end处要+1(绝对打卡)
然后,lca一定在最短路径上(一旦给断开了旅行计划就没了)
根据树上差分的性质,lca处加了两次,要减掉一次
但是在lca的父亲(1)处尚还残留一次多余的打卡
也要减掉一次,避免对祖先产生影响
复杂度
#include<bits/stdc++.h>
using namespace std;
int n,m;
struct akioii{
int v,nxt;
}e[200007];
int head[100007],cnt;
void addE(int u,int v){
e[++cnt]={v,head[u]};
head[u]=cnt;
}
int fa[100007][20],dis[100007],lg[100007];
void dfs1(int u,int pa){
dis[u]=dis[pa]+1;
fa[u][0]=pa;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==pa)
continue;
dfs1(v,u);
}
}
void init(){
dfs1(1,0);
lg[0]=-1;
for(int i=1;i<=100000;i++)
lg[i]=lg[i>>1]+1;
for(int i=1;i<=19;i++)
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
}
int lca(int x,int y){
if(dis[x]<dis[y])
swap(x,y);
for(int d=dis[x]-dis[y],i=lg[d];~i;i--)
if(d>>i&1)
x=fa[x][i];
if(x==y)
return x;
for(int i=19;~i;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int a[100007];
void dfs2(int u,int pa){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==pa)
continue;
dfs2(v,u);
a[u]+=a[v];
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
addE(u,v),addE(v,u);
}
init();
for(int i=1,s,t;i<=m;i++){
scanf("%d%d",&s,&t);
a[s]++,a[t]++;
int la=lca(s,t);
a[la]--,a[fa[la][0]]--;
}
dfs2(1,0);
for(int i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}