题目链接
有 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<
给定 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<
给一棵树,每一个节点是一个字符,每次询问 u , v u,v u,v,从 u u u沿最短路到 v v v形成一个字符串,问该字符串中有多少个子序列(不用连续)为 “ a b c b a ” “abcba” “abcba”。
参考自橘子猫的题解链接
首先将每一个节点到其叶子节点的区间用主席树来维护,我们要求 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