20190226
T1:
内存限制:32 MiB
时间限制:2000 ms
Luka 是一个画商,现在有 NNN 个顾客过来给她买画。每个顾客只能买彩画或者水墨画中的一种,并且每个人最多可以买 aia_iai 幅彩画或 bib_ibi 幅水墨画(每个顾客至少要买一副画)。
为了让 Luka 高兴,至少要有 CCC 人买彩画。由于每个顾客不断地修改购买需求, Luka 请你帮忙计算对于每次修改后,有多少种不同的方案使得 Luka 高兴。
1≤N,Q≤105,1≤C≤20,1≤ai,bi≤1091 \leq N,Q \leq 10^5 , 1 \leq C \leq 20,1 \leq a_i,b_i \leq 10^91≤N,Q≤105,1≤C≤20,1≤ai,bi≤109
题解
我们可以先算出所有买画的情况数,即为 ∏i=1n(ai+bi)\prod_{i=1}^n(a_i+b_i)∏i=1n(ai+bi)
考虑小于 ccc 的情况如何计算
可以得到一个很简单的 dpdpdp 式子
Fi,jF_{i,j}Fi,j 表示前 iii 个人, jjj 个人买了彩画的方案数
Fi,j=Fi−1,j×bi+Fi−1,j−1×aiF_{i,j}=F_{i-1,j}\times b_i+F_{i-1,j-1}\times a_iFi,j=Fi−1,j×bi+Fi−1,j−1×ai
然后将其对应到线段树上的一段区间,变成这一段区间内, jjj人选了彩画的方案数
类似一个卷积的形式,可以在 O(nlognc2)O(nlognc^2)O(nlognc2) 内得到答案
貌似有撤销 dpdpdp 的做法(%pjd szm,可惜我不会就不写啦
#include <cstdio>
using namespace std;
const int P=1e4+7,N=1e5+1;
int n,c,b[N],a[N],h[400001],pi[400001],q;
short g[400001][20];
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
void build(int k,int l,int r){
if (l==r){
h[k]=1;g[k][0]=(short)b[l];
g[k][1]=(short)a[l];
pi[k]=a[l]+b[l];
if (pi[k]>=P) pi[k]-=P;
return;
}
build(Ls,l,mid);build(Rs,mid+1,r);
for (int i=0;i<=h[Ls] && i<c;i++)
for (int j=0;j<=h[Rs] && i+j<c;j++){
g[k][i+j]=((int)g[k][i+j]+(int)g[Ls][i]*(int)g[Rs][j]%P)%P;
if (i+j>h[k]) h[k]=i+j;
}
pi[k]=pi[Ls]*pi[Rs]%P;
}
void update(int k,int l,int r,int x){
if (l==r){
g[k][0]=(short)b[l];
g[k][1]=(short)a[l];
pi[k]=a[l]+b[l];
if (pi[k]>=P) pi[k]-=P;
return;
}
if (mid>=x) update(Ls,l,mid,x);
else update(Rs,mid+1,r,x);
for (int i=0;i<=h[k];i++) g[k][i]=0;
for (int i=0;i<=h[Ls] && i<c;i++)
for (int j=0;j<=h[Rs] && i+j<c;j++)
g[k][i+j]=((int)g[k][i+j]+(int)g[Ls][i]*(int)g[Rs][j]%P)%P;
pi[k]=pi[Ls]*pi[Rs]%P;
}
int main(){
scanf("%d%d",&n,&c);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]),a[i]%=P;
for (int i=1;i<=n;i++)
scanf("%d",&b[i]),b[i]%=P;
build(1,1,n);scanf("%d",&q);
for (int i,sum;q--;){
scanf("%d",&i);
scanf("%d%d",&a[i],&b[i]);
a[i]%=P;b[i]%=P;update(1,1,n,i);
sum=pi[1];
for (int j=0;j<c;j++){
sum=sum-(int)g[1][j];
if (sum<0) sum+=P;
}
printf("%d\n",sum);
}
return 0;
}
T2
内存限制:256 MiB
时间限制:1000 ms
恶梦是一个登山爱好者,今天他来到了黄山。
俗话说的好,不走回头路。所以在黄山,你只能往前走,或者往上走。并且很显然的是,当你走到山脊的时候,你不能够往上走,你只能往前走一步再往上走。
抽象一点而言就是,你可以把黄山视为一个 N×NN \times NN×N 格点图,恶梦从 (0,0)(0,0)(0,0) 开始出发,要走到 (N,N)(N,N)(N,N) 。当他走到位置 (x,y)(x,y)(x,y) 的时候,它可以往 (x+1,y)(x + 1,y)(x+1,y),或 (x,y+1)(x,y+1)(x,y+1) 走。
并且当他走到 (x,x)(x,x)(x,x) 的时候,由于他已经处在了山脊上,所以他不能够往 (x,x+1)(x,x+1)(x,x+1) 方向上走。
当恶梦兴致勃勃准备开始爬山的时候,他的同伴告诉他,黄山由于年久失修,有一些位置出现了大坑,不能走。恶梦觉得更刺激了,但他想先知道他能有多少种方式走到黄山顶。
由于这个数字很大,所以你只需要将答案对 109+710^9+7109+7 取模输出即可。
N≤105,C≤103N \leq 10^5,C \leq 10^3N≤105,C≤103
题解
有点像流浪者那一题,不知道为什么并没有想出来(我太弱了
先不考虑不超过直线 y=xy=xy=x 的限制的做法
先把坑按照 xxx 排序,为了后续方便,我们将 (n,n)(n,n)(n,n) 也看成一个坑
设 FiF_iFi 表示前面没经过坑,到达第 iii 个坑的方案数
首先我们现将 (0,0)→(xi,yi)(0,0)\rightarrow(x_i,y_i)(0,0)→(xi,yi) 的方案数求出,即为 Cxi+yixiC_{x_i+y_i}^{x_i}Cxi+yixi
然后我们扣除不合法的方案,枚举前面的坑,设不合法的路径碰到第一个坑为j
那之后再怎么走也是不合法的(碰到坑了,所以要扣除 Fj∗Cxi+yi−xj+yjxi−xjF_j*C_{x_i+y_i-x_j+y_j}^{x_i-x_j}Fj∗Cxi+yi−xj+yjxi−xj
考虑限制,发现所有不合法的限制得路径上的点一定会经过直线 y=x+1y=x+1y=x+1
所以我们发现 (x1,y1)→(x2,y2)(x1,y1)\rightarrow(x2,y2)(x1,y1)→(x2,y2) ,不合法的部分等同于 (y1−1,x1+1)→(x2,y2)(y1-1,x1+1)\rightarrow(x2,y2)(y1−1,x1+1)→(x2,y2) 的路径数
#include <bits/stdc++.h>
using namespace std;
const int N=1005,X=2e5+5,P=1e9+7;
int n,m,f[N],jc[X],ny[X];
struct O{int x,y;}p[N];
inline bool M(O A,O B){
return A.x<B.x || A.x==B.x && A.y<B.y;
}
inline int C(int x,int y){
return 1ll*jc[x]*ny[y]%P*ny[x-y]%P;
}
inline int J(int X1,int Y1,int X2,int Y2){
int x=X2-X1,y=Y2-Y1;
if (x<0 || y<0) return 0;
return C(x+y,x);
}
inline int L(int X1,int Y1,int X2,int Y2){
return (J(X1,Y1,X2,Y2)-J(Y1-1,X1+1,X2,Y2)+P)%P;
}
int main(){
scanf("%d%d",&n,&m);jc[0]=ny[n+n]=1;
for (int i=1;i<=n+n;i++) jc[i]=1ll*i*jc[i-1]%P;
for (int x=jc[n+n],y=P-2;y;x=1ll*x*x%P,y>>=1)
if (y&1) ny[n+n]=1ll*ny[n+n]*x%P;
for (int i=n+n;i;i--) ny[i-1]=1ll*i*ny[i]%P;
for (int i=1;i<=m;i++) scanf("%d%d",&p[i].x,&p[i].y);
p[++m]=(O){n,n};sort(p+1,p+m+1,M);f[0]=-1;
for (int i=1;i<=m;i++) for (int j=0;j<i;j++)
f[i]=(f[i]-1ll*f[j]*L(p[j].x,p[j].y,p[i].x,p[i].y)%P+P)%P;
return printf("%d\n",f[m]),0;
}
T3
内存限制:512 MiB
时间限制:1000 ms
作为钦钦草原最绿的男人,杨某针每天都要开车巡视钦钦草原一圈。
钦钦草原由 nnn 个城市组成, mmm 条双向道路连接着它们。经过第 iii 条道路要花费的时间是 2i2^i2i 。
杨某针想要经过每条道路至少一次,在此基础上他想最小化他花费的时间。但作为曾经 CTSC 的 Cu 选手,他并不能很快地计算出这个问题。所以他向你求助。
n≤4×105,m≤5×105n \leq 4 \times 10^5,m \leq 5 \times 10^5n≤4×105,m≤5×105
题解
每条边不会经过三次,所以一条边只会经过一次或两次
这题要经过一个欧拉回路,所以如果一个点是奇点的话,我们要为其加上边
那要从哪里选边呢?我们可以建最小生成树,因为如果考虑当前边 (x,y)(x,y)(x,y) ,如果 xxx 和 yyy 在同一联通块内,那我们可以选择 x→yx \rightarrow yx→y路径上的边,这样总权值会比这单条边来的更小
所以建完最小生成树,从叶子节点到根,如果当前点是奇点,那其上面那条边要选
#include <cstdio>
const int P=1e9+7,N=4e5+5;bool d[N];
int n,m,pi=1,ans,head[N],V[N*2],W[N*2],nex[N*2],t,f[N];
int get(int x){return f[x]==x?x:f[x]=get(f[x]);}
void add(int u,int v,int w){
V[++t]=v;nex[t]=head[u];head[u]=t;W[t]=w;
}
void dfs(int x,int fa){
for (int i=head[x];i;i=nex[i]) if (V[i]!=fa){
dfs(V[i],x);
if (d[V[i]]) (ans+=W[i])%=P,d[x]^=1;
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) f[i]=i;
for (int x,y,X,Y,i=1;i<=m;i++){
scanf("%d%d",&x,&y);(pi+=pi)%=P;
(ans+=pi)%=P;d[x]^=1;d[y]^=1;
if ((X=get(x))!=(Y=get(y)))
f[Y]=X,add(x,y,pi),add(y,x,pi);
}
return dfs(1,0),printf("%d\n",ans),0;
}