匹配
给出一个(n(nleq80))个点对(n)个点的带权二分图,求所有最大权匹配的交集。
先求出一个最大权匹配,然后枚举每一条匹配中的边,检验删除该边后是否还能形成最大权匹配。如果能则说明该边不在交集中,否则一定在交集中。
电源插排
一个长度为(n(nleq10^9))的插排,初始时是空的。进行(Q(Qleq10^5))次操作,操作有三种:
- 向整个插排上最大的空区间(相等取最右)的中间(也取右)插上一个插头(x)。
- 询问区间([L,R])中有几个插头。
- 拔出插头(x)。
用动态开点线段树来做即可。每条线段维护四个值:左起最长空区间的右端点,区间最长空区间的左、右端点,右起最长空区间的左端点。再记录一下插头(x)的位置就可以删除啦。
拼图
给出(n(nleq16))块四连通碎片,求有多少种方案将他们一起拼成一个(4 imes4)的正方形。若没有,输出No Solution
;只有一种,输出Yes, only one!
和这个方案;否则输出Yes, many!
。
似乎直接爆搜即可,优化一点可以按块由大到小搜索,再优化可以搞搞状压什么的。
上升子序列
给出一个(n(nleq10^5))长度的数列,求该数列有多少个不同的长度至少为(2)的上升子序列,答案对(10^9+7)取模。两个子序列(a,b)被认为不同当且仅当(exists i,a_i eq b_i)。
记录(cnt[i])表示目前以数字(i)为结尾的不同上升子序列有多少个。当做到(a_i)时,(cnt[a_i])变为(1+sum_{i=1}^{a[i]-1}cnt[i])个,额外的(1)表示只有一个数的序列({a_i}),求和表示(a_i)能够接在多少个上升子序列后。要注意(cnt[a_i])是变为,而不是加上,因为如果存在(a_{i'}=a_i(i'<i)),那么以(a_{i'})结尾的上升子序列一定出现在以(a_i)结尾的上升子序列中(把原序列中的(a_{i'})换成(a_i))。
那么我们需要支持单点修改和前缀求和,可以用树状数组解决。
Alice and Bob
对于序列({x_n}(nleq10^5)),记(a_i)表示以(x_i)结尾的最长上升子序列长度,(b_i)表示以(x_i)开头的最长下降子序列长度。给出({a_n}),求(sum_{i=1}^nb_i)的最大值。
若在(a_1..a_{i-1})中有(k)个大于(a_i),那么说明在(x_1..x_{i-1})中,至少有(k)个大于(x_i),至少有(a_i-1)个小于(x_i)。
我没太看懂的题解:现在我们已知了一些大小关系。对于未知的关系,我们默认左边大于右边。构造序列并求(sum_{i=1}^nb_i)即可。
电影评分
搞一个电影评分系统,进行(n(nleq10^4))次操作,操作有三种:
- 发布一部编号为(ID)的电影,有(x(xleq5))个主演({actor_x}),其评分为与其有共同主演的最新电影的评分,若没有则为(0)。
- 查询排名为(x)的电影的编号。排名以评分为第一关键字,发布时间为第二关键字。
- 将电影(ID)的评分调整为原评分与(x)的平均数。
题解:将题目中的分数分为整数部分和小数部分,将小数部分写成二进制形式,此时可以使用后缀平衡树维护小数之间的大小关系。将小数部分和整数部分结合即可比较分数的大小关系。没大看懂,也不会后缀平衡树。
Code
//匹配
#include <cstdio>
#include <cstring>
#include <queue>
int const N=300;
int const INF=0x3F3F3F3F;
int min(int x,int y) {return x<y?x:y;}
int n;
int h[N],cnt;
struct edge{int u,v,c,w,nxt;} ed[N*N];
void edAdd(int u,int v,int c,int w)
{
cnt++; ed[cnt].u=u,ed[cnt].v=v,ed[cnt].c=c,ed[cnt].w=w,ed[cnt].nxt=h[u],h[u]=cnt;
cnt++; ed[cnt].u=v,ed[cnt].v=u,ed[cnt].c=0,ed[cnt].w=-w,ed[cnt].nxt=h[v],h[v]=cnt;
}
int s,t;
int dst[N],path[N];
std::queue<int> Q; bool inQ[N];
void clear() {for(int i=2;i<=cnt;i++) ed[i].c=i&1?0:ed[i].c+ed[i^1].c;}
bool SPFA()
{
for(int u=s;u<=t;u++) dst[u]=-INF,path[u]=0;
memset(inQ,false,sizeof inQ);
dst[s]=0; Q.push(s),inQ[s]=true;
while(!Q.empty())
{
int u=Q.front(); Q.pop(),inQ[u]=false;
for(int i=h[u];i;i=ed[i].nxt)
{
int v=ed[i].v,w=ed[i].w;
if(ed[i].c&&dst[u]+w>dst[v])
{
dst[v]=dst[u]+w,path[v]=i;
if(!inQ[v]) Q.push(v),inQ[v]=true;
}
}
}
return dst[t]>-INF;
}
int maxFl()
{
int cost=0;
while(SPFA())
{
int fl=INF;
for(int i=path[t];i;i=path[ed[i^1].v]) fl=min(fl,ed[i].c);
for(int i=path[t];i;i=path[ed[i^1].v]) ed[i].c-=fl,ed[i^1].c+=fl;
cost+=fl*dst[t];
}
return cost;
}
int cntM,M[N];
int main()
{
freopen("match.in","r",stdin);
freopen("match.out","w",stdout);
scanf("%d",&n);
s=0,t=n+n+1; cnt=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) {int x; scanf("%d",&x); edAdd(i,n+j,1,x);}
for(int i=1;i<=n;i++) edAdd(s,i,1,0),edAdd(n+i,t,1,0);
int ans=maxFl(); printf("%d
",ans);
cntM=0;
for(int u=1;u<=n;u++)
for(int i=h[u];i;i=ed[i].nxt) if(!ed[i].c&&ed[i].v!=s) M[++cntM]=i;
for(int k=1;k<=cntM;k++)
{
clear(),ed[M[k]].c=0;
int cost=maxFl();
ed[M[k]].c=1;
if(cost<ans) printf("%d %d
",ed[M[k]].u,ed[M[k]].v-n);
}
return 0;
}
//电源插排
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
inline char gc()
{
static char now[1<<16],*S,*T;
if(S==T) {T=(S=now)+fread(now,1,1<<16,stdin); if(S==T) return EOF;}
return *S++;
}
inline int read()
{
int x=0; char ch=gc();
while(ch<'0'||'9'<ch) ch=gc();
while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
return x;
}
int const Q=1e5+10;
int n,q; map<int,int> pos;
#define s sg[s0]
int rt,sgCnt;
struct seg{int eptL0,eptR0,eptL,eptR,sum; int Lc,Rc;} sg[40*Q];
void create(int s0,int L0,int R0) {s.eptL0=s.eptR=R0,s.eptR0=s.eptL=L0,s.sum=0;}
void update(int s0,int L0,int R0)
{
int mid=L0+R0>>1; seg Ls=sg[s.Lc],Rs=sg[s.Rc];
s.eptL0=Ls.eptL0; if(Ls.eptL0==mid) s.eptL0=Rs.eptL0;
s.eptR0=Rs.eptR0; if(Rs.eptR0==mid+1) s.eptR0=Ls.eptR0;
int len1=Ls.eptR-Ls.eptL+1,len2=Rs.eptL0-Ls.eptR0+1,len3=Rs.eptR-Rs.eptL+1;
int mx=max(len2,max(len1,len3));
if(len3==mx) s.eptL=Rs.eptL,s.eptR=Rs.eptR;
else if(len2==mx) s.eptL=Ls.eptR0,s.eptR=Rs.eptL0;
else if(len1==mx) s.eptL=Ls.eptL,s.eptR=Ls.eptR;
s.sum=Ls.sum+Rs.sum;
}
int L,R;
void add(int s0,int L0,int R0,int v)
{
if(L<=L0&&R0<=R)
{
s.sum=v;
if(v==1) s.eptL0=L0-1,s.eptR0=R0+1,s.eptL=L0+1,s.eptR=L0;
else s.eptL0=s.eptR0=s.eptL=s.eptR=L0;
return;
}
int mid=L0+R0>>1;
if(!s.Lc) create(s.Lc=++sgCnt,L0,mid);
if(!s.Rc) create(s.Rc=++sgCnt,mid+1,R0);
if(L<=mid) add(s.Lc,L0,mid,v); if(mid<R) add(s.Rc,mid+1,R0,v);
update(s0,L0,R0);
}
int query(int s0,int L0,int R0)
{
if(s0==0) return 0;
if(L<=L0&&R0<=R) return s.sum;
int mid=L0+R0>>1; int res=0;
if(L<=mid) res+=query(s.Lc,L0,mid);
if(mid<R) res+=query(s.Rc,mid+1,R0);
return res;
}
int main()
{
freopen("switch.in","r",stdin);
freopen("switch.out","w",stdout);
n=read(),q=read();
create(rt=++sgCnt,1,n);
for(int i=1;i<=q;i++)
{
int k=read();
if(k==0) {L=read(),R=read(); printf("%d
",query(rt,1,n));}
else
{
L=R=(sg[rt].eptL+sg[rt].eptR-1)/2+1;
if(pos[k]) L=R=pos[k],add(rt,1,n,0),pos[k]=0;
else add(rt,1,n,1),pos[k]=L;
}
}
return 0;
}
//上升子序列
#include <cstdio>
#include <algorithm>
using namespace std;
int const N=1e5+10;
int const H=1e9+7;
int n,a[N]; int n0,map[N];
int tr[N];
void add(int x,int v) {while(x<=n0) tr[x]=(tr[x]+v)%H,x+=x&(-x);}
int sum(int x) {int res=0; while(x) res=(res+tr[x])%H,x-=x&(-x); return res;}
int pre[N];
int main()
{
freopen("subsequence.in","r",stdin);
freopen("subsequence.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),map[i]=a[i];
sort(map+1,map+n+1); n0=unique(map+1,map+n+1)-(map+1);
for(int i=1;i<=n;i++) a[i]=lower_bound(map+1,map+n0+1,a[i])-map;
for(int i=1;i<=n0;i++) pre[i]=0;
for(int i=1;i<=n;i++)
{
int res=sum(a[i]-1)+1;
add(a[i],res-pre[a[i]]); pre[a[i]]=res;
}
printf("%d
",sum(n0)-n0);
return 0;
}