A Stickers and Toys
思路:水题,保证取到就是在max(a,b)中然后减去两者都有的那部分-(a+b-n)再+1无论如何都可以满足
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
int main()
{
ll t,n,s,q;
scanf("%lld",&q);
while(q--)
{
scanf("%lld%lld%lld",&n,&s,&t);
printf("%lld",max(s,t)-(s+t-n)+1);
}
return 0;
}
B.
思路:预先开个数组存储每个字母第i次出现的位置,然后扫的时候不断相应取max即可 o(n)可过
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#define ll long long
using namespace std;
vector<int>v[27];
int a[27];
int main()
{
int n,m,q;
string s;
cin>>n;
cin>>s;
for(int i=0;i<s.size();++i)
{
v[s[i]-'a'].push_back(i);
}
cin>>q;
while(q--)
{
memset(a,0,sizeof(a));
string x;
cin>>x;
int ans=0;
for(int i=0;i<x.size();++i)
{
ans=max(ans,v[x[i]-'a'][a[x[i]-'a']]+1);
a[x[i]-'a']++;
}
cout<<ans<<"\n";
}
return 0;
}
C.
构造题,首先考虑构造数组a[i]=(a[i]<=a[i+1]),然后扫一遍0的,不存在的情况就是他的区间中a[i]没有一个0,a[i]=1时就和前面的一样,否则就-1,注意到数字一定要>=1,所以第一位从n开始构造
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
const int maxn=1010;
using namespace std;
int is[maxn];
int t[maxn],l[maxn],r[maxn];
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<m;++i)
{
scanf("%d%d%d",&t[i],&l[i],&r[i]);
l[i]--,r[i]--;
if(t[i]==1)
{
for(int j=l[i];j<r[i];++j)
is[j]=1;
}
}
for(int i=0;i<m;++i)
{
int flag=0;
if(!t[i])
{
for(int j=l[i];j<r[i];++j)
if(!is[j])
{
flag=1;
break;
}
if(!flag)
{
puts("NO");
return 0;
}
}
}
puts("YES");
int ans=n;
printf("%d ",ans);
for(int i=1;i<n;++i)
{
if(is[i-1])
printf("%d ",ans);
else
printf("%d ",--ans);
}
cout<<endl;
}
D.
思路:一次操作等同于左边比右边大的话不断两两交换,所以a[i]能到最前面的条件(指前面的最前面已经满足了)是(1,n)中它为最小(前面最小的被打过标记),所以建个线段树,从前往后扫,用队列记录出现位置,一旦判断过,就打个最大值标记
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
#define ls rt<<1
#define rs rt<<1|1
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int maxn=3e5+5;
int tr[maxn<<2];
queue<int>q[maxn];
int a[maxn],b[maxn];
const int inf=0x3f3f3f3f;
void build(int rt,int l,int r)
{
if(l==r)
{
tr[rt]=a[l];
q[a[l]].push(l);
return;
}
int mid=l+r>>1;
build(lson);
build(rson);
tr[rt]=min(tr[ls],tr[rs]);
}
void update(int rt,int l,int r,int x,int k)
{
if(l==r)
{
tr[rt]=k;
return;
}
int mid=l+r>>1;
if(x<=mid)update(lson,x,k);
else update(rson,x,k);
tr[rt]=min(tr[ls],tr[rs]);
}
int query(int rt,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
{
return tr[rt];
}
int mid=l+r>>1;
int val=(1<<30);
if(x<=mid)val=min(val,query(lson,x,y));
if(y>mid)val=min(val,query(rson,x,y));
return val;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
while(!q[a[i]].empty())
q[a[i]].pop();
}
build(1,1,n);
for(int i=1;i<=n;++i)
scanf("%d",&b[i]);
int flag=1;
for(int i=1;i<=n;++i)
{
int k;
if(!q[b[i]].empty())
{
k=q[b[i]].front();
q[b[i]].pop();
}
else
{
flag=0;
break;
}
int minn=query(1,1,n,1,k);
if(minn!=b[i])
{
flag=0;
break;
}
else update(1,1,n,k,inf);
}
if(flag)puts("YES");
else puts("NO");
}
return 0;
}
E.
二次扫描+换根法板子题
可以发现一旦根确定了,答案也是确定的,所以只要判断每个根即可,而且发现答案是各个结点子树大小之和
直接两次dfs,第一次处理所有结点的子树大小,第二次换根的时候只会影响到换的两个结点的子树的大小来影响答案,具体看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
const int maxn=2e5+10;
using namespace std;
int head[maxn],next1[maxn<<1],ver[maxn<<1],tot=0;
int size[maxn],n;
ll ans;
void add(int x,int y)
{
ver[++tot]=y,next1[tot]=head[x],head[x]=tot;
}
void dfs(int now,int fa)
{
size[now]=1;
for(int i=head[now];i;i=next1[i])
{
int y=ver[i];
if(y==fa)continue;
dfs(ver[i],now);
size[now]+=size[y];
}
ans+=size[now];
}
void dfs2(int now,int fa,ll num)
{
ans=max(ans,num);
for(int i=head[now];i;i=next1[i])
{
if(ver[i]==fa)continue;
dfs2(ver[i],now,num+n-2*size[ver[i]]);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;++i)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
ans=0;
dfs(1,-1);
dfs2(1,-1,ans);
printf("%lld\n",ans);
return 0;
}