题目链接
D 羊吃草(二分图最大匹配)
题意:
有 n n n个羊, m m m个草,每只羊可以吃 [ l i , r i ] [l_i,r_i] [li?,ri?]的草,有 Q Q Q次询问,每次询问 [ l i , r i ] [l_i,r_i] [li?,ri?]区间的草最多可以给多少只羊吃,每次询问独立。 ( n , m <
= 400 ) (n,m<
=400) (n,m<=400)
思路:
我们可以从草往羊进行连边,那么每次询问就是求 [ L i , R i ] [L_i,R_i] [Li?,Ri?]区间的草的最大匹配,用匈牙利算法就行。
代码:
#include
using namespace std;
const int N=500;
vectore[N];
int vis[N];
int match[N];
bool pp(int u,int time){
for(int v:e[u]){
if(vis[v]==time)continue;
vis[v]=time;
if(match[v]==-1||pp(match[v],time)){
match[v]=u;
return 1;
}
}
return 0;
}
int n,m;
int a[N],b[N];
int main()
{
cin>>n>>m;
for(int i=1;
i<=n;
i++)cin>>a[i];
for(int i=1;
i<=n;
i++)cin>>b[i];
int cnt=0;
for(int i=1;
i<=n;
i++){
for(int j=a[i];
j<=b[i];
j++)e[j].push_back(i);
}
for(int i=1,l,r;
i<=m;
i++){
cin>>l>>r;
for(int j=1;
j<=n;
j++)match[j]=-1;
int ans=0;
for(int j=l;
j<=r;
j++){
if(pp(j,++cnt))ans++;
}
cout<
E 数列(二分+思维) 题意:
给定 n , m n,m n,m要求构造一个长度为 n n n 正整数序列 { a i } \{a_i\} {ai?},使得 ∑ a i < = m \sum{a_i}< =m ∑ai?<=m,并且使得价值最大,价值为 a [ i ] = a [ i ? 1 ] + 1 a[i]=a[i-1]+1 a[i]=a[i?1]+1的出现次数。
思路:
我们可以发现序列最终一个是 1 , 2 , 3....1 , 2 , 3.....1 , 2 , 3... 1,2,3....1,2,3.....1,2,3... 1,2,3....1,2,3.....1,2,3...这种形式,那么我们假设我们将数列分成 k k k段,每一段的长度为 l e n i len_i leni?,那么每一段的和就是 l e n i ? ( l e n i + 1 ) / 2 len_i*(len_i+1)/2 leni??(leni?+1)/2,价值为 n ? k n-k n?k,我们要使得和尽量小,就是让每一段的长度尽量平均分配,那么对于每一个 k k k我们都可以求出其数字的总和(通过k , n / k , n % k k,n/k,n\%k k,n/k,n%k),我们只要求出满足题意的最小的k即可使得价值最大,并且还原序列。
代码:
#include
#define ll long long
using namespace std;
const int N=1e5+10;
ll n,m;
vector v[N];
bool check(ll x){
int len=n/x,yu=n%x;
ll temp=x*(len)*(len+1)/2+yu*(len+1);
return temp<=m;
}
int main()
{
scanf("%d%d",&n,&m);
ll l=1,r=n,ans;
while(l<=r){
ll mid=(l+r)/2;
if(check(mid)){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
ll len=n/ans,yu=n%ans;
for(ll i=1;
i<=ans;
i++){
for(ll j=1;
j<=len;
j++)v[i].push_back(j);
}
for(ll i=1;
i<=yu;
i++){
v[i].push_back(len+1);
}
for(ll i=1;
i<=ans;
i++){
for(ll x:v[i]){
cout<
F ABCBA(主席树) 题意:
给一棵树,每一个节点是一个字符,每次询问 u , v u,v u,v,从 u u u沿最短路到 v v v形成一个字符串,问该字符串中有多少个子序列(不用连续)为 “ a b c b a ” “abcba” “abcba”。
思路:
【比赛题解|牛客练习赛 51 (DEF题解)】参考自橘子猫的题解链接
首先将每一个节点到其叶子节点的区间用主席树来维护,我们要求 a b c b a abcba abcba的出现次数,由于其是一个回文串,我们可以直接维护 a , b , c , b a , a b , b c , c b , a b c , c b a , b c b , a b c b , b c b a , a b c b a a,b,c,ba,ab,bc,cb,abc,cba,bcb,abcb,bcba,abcba a,b,c,ba,ab,bc,cb,abc,cba,bcb,abcb,bcba,abcba的数量。
其中 a b c b a abcba abcba=&ls.abcba+rs.abcba+ls.a*rs.bcba+ls.ab*rs.cba+ls.abc*rs.ba+ls.abcb*rs.a&
剩下的也类似。
那么在查询 u , v u,v u,v的时候我们只要求出 l c a ( u , v ) lca(u,v) lca(u,v),把两端进行合并即可,要注意当 l c a lca lca不是 u , v u,v u,v时,合并的时候 u → l c a u \to lca u→lca的字符串应该时反的。
学习大佬博客中对节点加法运算符的重定义是真的很好用。
代码:
#include
using namespace std;
#define ll long long
const int N=3e4+10;
const int mod=10007;
char s[N];
int n,q,u,v;
vectorE[N];
//主席树
int ls[N*20],rs[N*20];
struct node{
int a,b,c,ba,ab,bc,cb,abc,cba,bcb,abcb,bcba,abcba;
}e[N*20];
node operator +(const node &x,const node &y){//要小心检查,错了一个找了1个小时
node z;
z.a=(x.a+y.a)%mod;
z.b=(x.b+y.b)%mod;
z.c=(x.c+y.c)%mod;
z.ba=(x.ba+y.ba+x.b*y.a)%mod;
z.ab=(x.ab+y.ab+x.a*y.b)%mod;
z.bc=(x.bc+y.bc+x.b*y.c)%mod;
z.cb=(x.cb+y.cb+x.c*y.b)%mod;
z.abc=(x.abc+y.abc+x.a*y.bc+x.ab*y.c)%mod;
z.cba=(x.cba+y.cba+x.c*y.ba+x.cb*y.a)%mod;
z.bcb=(x.bcb+y.bcb+x.b*y.cb+x.bc*y.b)%mod;
z.abcb=(x.abcb+y.abcb+x.a*y.bcb+x.ab*y.cb+x.abc*y.b)%mod;
z.bcba=(x.bcba+y.bcba+x.b*y.cba+x.bc*y.ba+x.bcb*y.a)%mod;
z.abcba=(x.abcba+y.abcba+x.a*y.bcba+x.ab*y.cba+x.abc*y.ba+x.abcb*y.a)%mod;
return z;
}
int rt[N*20],cnt=0;
void update(int &root,int pre,int l,int r,int pos,char c){
root=++cnt;
ls[root]=ls[pre];
rs[root]=rs[pre];
if(l==r){
if(c=='A')e[root].a=1;
else if(c=='B')e[root].b=1;
else if(c=='C')e[root].c=1;
return ;
}
int mid=(l+r)/2;
if(pos<=mid)update(ls[root],ls[pre],l,mid,pos,c);
else update(rs[root],rs[pre],mid+1,r,pos,c);
e[root]=e[ls[root]]+e[rs[root]];
}node query(int root,int l,int r,int LL,int RR){
if(l>=LL&&r<=RR){
return e[root];
}
int mid=(l+r)/2;
if(LL>mid)return query(rs[root],mid+1,r,LL,RR);
else if(RR<=mid)return query(ls[root],l,mid,LL,RR);
else return query(ls[root],l,mid,LL,RR)+query(rs[root],mid+1,r,LL,RR);
//加法很好用
}
//树剖int deep[N],fa[N],son[N],tot[N];
int dfs1(int u,int f,int dep){
deep[u]=dep;
fa[u]=f;
tot[u]=1;
int pd=-1;
update(rt[u],rt[f],1,n,deep[u],s[u]);
//以深度做为位置
for(int v:E[u]){
if(v==f)continue;
tot[u]+=dfs1(v,u,dep+1);
if(tot[v]>pd)pd=tot[v],son[u]=v;
}
return tot[u];
}
int top[N],idx[N],cnt1=0;
void dfs2(int u,int f){
idx[u]=++cnt1;
top[u]=f;
if(!son[u])return ;
dfs2(son[u],f);
for(int v:E[u]){
if(!idx[v])dfs2(v,v);
}
}
int Lca(int x,int y){
while(top[x]!=top[y]){
if(deep[top[x]]deep[y])swap(x,y);
return x;
}int main()
{
scanf("%d%d",&n,&q);
scanf("%s",s+1);
for(int i=1;
i
推荐阅读
- 笔记|C语言数据结构——二叉树的顺序存储和二叉树的遍历
- C语言学习(bit)|16.C语言进阶——深度剖析数据在内存中的存储
- 数据结构和算法|LeetCode 的正确使用方式
- 先序遍历 中序遍历 后序遍历 层序遍历
- 数据结构|C++技巧(用class类实现链表)
- 数据结构|贪吃蛇代码--c语言版 visual c++6.0打开
- 算法|算法-二分查找
- 数据结构学习指导|数据结构初阶(线性表)
- leetcode题解|leetcode#106. 从中序与后序遍历序列构造二叉树
- java|ObjectOrientedProgramming - 面向对象的编程(多态、抽象类、接口)- Java - 细节狂魔