Pro A 106
-
题意:给定 (n) ,求满足 (3^a+5^b=n) 的任意一组解。
-
数据范围:(nleq 10^{18})
-
做法:
预处理出值在 (10^{18}) 次方以内的 (3) 的幂和 (5) 的幂然后直接枚举。
-
代码:
const unsigned long long MAX=1e18; using namespace std; int d3,d5; unsigned long long _3[100],_5[100],n; void Prework(){ _3[0]=_5[0]=1; for(int i=1;_3[i-1]<=MAX;d3=i-1,++i) _3[i]=_3[i-1]*3ll; for(int i=1;_5[i-1]<=MAX;d5=i-1,++i) _5[i]=_5[i-1]*5ll; } int main(){ Prework();r(n); for(int i=1;i<=d3;++i) for(int j=1;j<=d5;++j) if(_3[i]+_5[j]==n) printf("%d %d ",i,j),exit(0); puts("-1"); return 0; }
Pro B Values
-
题意:给定一张 (n) 个点 (m) 条边的图,每个点有权值 (a_i) ,以及期望变成的权值 (b_i) 。每次你可以进行一次如下操作:选取一条边 (e:u ightarrow v) ,将 (u) 权值加一 (v) 的权值减一或者将 (v) 的权值减一 (u) 的权值加一。每条边可以进行无限次操作。
-
数据范围:(n,mleq 10^5,a_i,b_ileq 10^9)
-
做法:
在一个连通块内的点点权是可以任意传递的,所以只需要判断连通块内的点的 (sum a) 与 (sum b) 是否相等。
-
代码:
const int N=2e5+5,M=2e5+5; using namespace std; int n,m,num; int vis[N],head[N],nt[M<<1],to[M<<1]; long long a[N],b[N],tota,totb; void Add(int x,int y){ ++num;nt[num]=head[x];head[x]=num;to[num]=y; ++num;nt[num]=head[y];head[y]=num;to[num]=x; } void DFS(int p){ vis[p]=1;tota+=a[p],totb+=b[p]; for(int i=head[p];i;i=nt[i]) if(!vis[to[i]]) DFS(to[i]); } int main(){ r(n),r(m); for(int i=1;i<=n;++i) r(a[i]); for(int i=1;i<=n;++i) r(b[i]); for(int i=1,x,y;i<=m;++i) r(x),r(y),Add(x,y); for(int i=1;i<=n;++i){ if(vis[i]) continue; tota=0;totb=0;DFS(i); if(tota!=totb) puts("No"),exit(0); } puts("Yes"); return 0; }
Pro C Solutions
-
题意:有一个题目:给定 (n) 个闭区间 ([l_i,r_i]) ,求在满足两两不相交的情况下选出最多的区间。Takahashi 写了个程序:将 (r_i) 按升序排列后,每次从左往右能选就选;Aoki 写了个程序:将 (l_i) 按升序排序后,每次从左往右能选就选。你需要构造这 (n) 个闭区间,使得 Takahashi 的答案比 Aoki 的答案恰好多 (m) 。无解输出
-1
。 -
数据范围:(nleq 10^5,-nleq m leq n,l_i,r_ileq 10^9,l_i eq r_j,l_i eq l_j(i eq j),r_i eq r_j(i eq j))
-
做法:
Takahashi 的做法是正确做法,所以 (m) 只能为正数。当 (mge n-1) 时易得不存在解(因为此时至少需要满足 (n) 个区间均互不相交,这时 Aoki 也可以得出正解)。当 (mleq n-2) 时,我们可以构造出 (m+2) 个区间,满足:第一个区间 Aoki 必然会选,并且选完后就不能再选剩下的 (m+1) 个了;而 Takahashi 则会选择 (m+1) 个区间而放弃第一个区间。至于剩下的区间,我们让两人都选择就可以了。
-
代码:
int n,m; void Printres(int k){ int L=1e7; for(int i=1;i<=k;++i,L+=2) w(L),putchar(' '),w(L+1),putchar(10); } void Solve(int d){ puts("1 1000000"); int L=2; for(int i=1;i<=d+1;++i,L+=2) w(L),putchar(' '),w(L+1),putchar(10); Printres(n-(d+2)); } int main(){ r(n),r(m); if(n==1&&m==0) return 0*puts("1 1"); if(m<0||m>=n-1) return 0*puts("-1"); Solve(m); return 0; }
Pro D Powers
-
题意:给定 (n,p) 和长度为 (n) 的序列 ({a}) ,对于 (forall xin[1,p]) ,求下式的值:
[sum_{i=1}^{n-1}sum_{j=i+1}^{n}(a_i+a_j)^x pmod{998244353} ] -
数据范围:(2leq nleq 2 imes 10^5,1leq pleq 300)
-
做法:
对原式进行变形:
[egin{aligned} sum_{i=1}^{n-1}sum_{j=i+1}^{n}(a_i+a_j)^x&=sum_{i=1}^{n}sum_{j=1}^{n}(a_i+a_j)^x-2^xsum_{i=1}^{n}a_i^x\ &=sum_{i=1}^{n}sum_{j=1}^{n}sum_{k=0}^{x}pmatrix{x\k} a_i^ka_j^{x-k}-cdots\ &=sum_{k=0}^{x}pmatrix{x\k}sum_{i=1}^{n}a_i^ksum_{j=1}^{n}a_j^{x-k}-cdots end{aligned} ] 只要对于 (kin[1,p]) 处理出 (sumlimits_{i=1}^{n} a_i^k) 就行了。复杂度 (mathcal{O}(nk)) 。
-
代码:
const int N=2e5+5,K=3e2+5,Mod=998244353,inv=(Mod+1)/2; using namespace std; int n,k; int a[N],bin[K],s[N][K],c[K][K]; void Prework(){ bin[0]=1; for(int i=1;i<=k;++i) bin[i]=bin[i-1]*2ll%Mod; c[0][0]=c[1][0]=1; for(int i=1;i<=k;++i,c[i][0]=1) for(int j=1;j<=i;++j) c[i][j]=(c[i-1][j-1]+c[i-1][j])%Mod; for(int i=1;i<=n;++i) s[i][0]=1; for(int i=1;i<=n;++i) for(int j=1;j<=k;++j) s[i][j]=1ll*s[i][j-1]*a[i]%Mod; for(int i=0;i<=k;++i) for(int j=2;j<=n;++j) s[j][i]=(s[j-1][i]+s[j][i])%Mod; } int solve(int d){ int res=0; for(int i=0;i<=d;++i) res=(res+1ll*c[d][i]*s[n][i]%Mod*s[n][d-i]%Mod)%Mod; return res; } int main(){ r(n),r(k); for(int i=1;i<=n;++i) r(a[i]); Prework(); for(int i=1;i<=k;++i){ int ans=solve(i); ans=(ans-1ll*bin[i]*s[n][i]%Mod+Mod)%Mod; w(1ll*ans*inv%Mod);putchar(10); } return 0; }
Pro E Medals
-
题意:有 (n) 个工人,每个工人都会工作 (a_i) 天,休息 (a_i) 天,再工作 (a_i) 天 (cdotscdots) 你现在需要给这 (n) 个工人每人发 (k) 个奖牌,但是你每天只能给一个人奖牌。请问你至少要多少天才能达成目标。
-
数据范围:(nleq 18,kleq 10^5,a_ileq 10^5)
-
做法:
考虑构建二分图,左边有 (n imes k) 个节点钦定为某个人的奖牌,右边设有 (x) 个节点,第 (i) 个表示第 (i) 天。代表钦定为第 (i) 个人的奖牌的节点向第 (i) 个人的工作日连边。原问题转化为:(x) 至少为多少时,该二分图有最大完备匹配。
Hall 定理:二分图存在最大完备匹配的充要条件是与某一侧的任意 (k) 个点相连的另一侧的节点 (ge k) 个。
左部节点相当于是 (n) 块,每一块的 (k) 个节点与右部相连的点集完全一致,所以可以一选俱选。因此,当我们选择了 (i) 个人时,右部的节点必须有 (ge i imes k) 个与之钦定的奖牌相连——换句话说,这 (i) 个人出现的天数的并集大小应 (ge i imes k) 。考虑补集转化 ,总天数为 (x) 时,任意 (i) 个人出现的天数的并集大小等于 (x-) 仅有剩下的 (n-i) 个人出现的天数。至于求仅有 (x) 个人出现的天数,我们可以先求出每一天的工作的人的集合,再用桶的方法求出每个集合工作的天数,再做一次子集和,就 OK 了。所以我们二分天数 (x) ,每次判定对于任意工作者的子集是否满足 Hall 定理即可。复杂度 (mathcal{O}(n2^nlog{nk})) 。
-
代码:
const int U=(1<<20),N=20,K=1e5+5,M=3*N*K; using namespace std; int n,k; int a[N],t[U],bit[U],work[M]; int main(){ r(n),r(k);int Max=n*k*3; for(int i=1;i<=n;++i) r(a[i]); for(int i=1;i<=n;++i) for(int j=1;j<=Max;++j) if((((j-1)/a[i])&1)==0) work[j]|=(1<<i-1); int l=n*k,r=Max,u=(1<<n)-1,ans=0; for(int i=1;i<=u;++i) bit[i]=bit[i&(i-1)]+k; while(l<=r){ int mid=l+r>>1; memset(t,0,sizeof t); for(int i=1;i<=mid;++i) ++t[work[i]]; for(int i=1;i<=n;++i){ int d=(1<<i-1)-1; for(int j=0;j<=u;++j){ int cur=(j&d)|((j&~d)<<1); t[cur|(1<<i-1)]+=t[cur]; } } bool flag=true; for(int i=1;i<=u&&flag;++i) flag&=((mid-t[i^u])>=(bit[i])); if(flag) ans=mid,r=mid-1; else l=mid+1; } w(ans); return 0; }
Pro F Figures
-
题意:有 (n) 个有标号的点,每个点规定度数至多为 (a_i) ,且这 (a_i) 个度数接口互不相同,求生成树个数在 ( ext{mod } 998244353) 意义下的值。
-
数据范围:(2leq nleq 10^5,1leq a_i< 998244353)
-
做法:
假设最后点 (i) 实际上的度数为 (d_i+1) ,那么每次形成方案都会对答案造成的额外贡献为 (a_i^{underline{d_i+1}}) 。因为 prufer 序列与生成树一一对应,所以最终答案应该等于
[sum_{sum_{d_i}=n-2}pmatrix{n-2\d_1,d_2,cdots,d_n}prod_{i=1}^{n}a_i^{underline{d_i+1}} ] 观察式子可知,我们用生成函数时应使用指数型生成函数。设 (F_k(x)) 表示当度数至多为 (k) 时的生成函数(次数为 (i) 的项的系数为该点的度数为 (i+1) 时的贡献),则有
[egin{aligned} F_k(x)&=sum_{i=0}^{k-1}frac{k^{underline{i+1}}}{i!}x^i\ &=sum_{i=0}^{k-1}kfrac{(k-1)!}{i!(k-i-1)!}x^i\ &=ksum_{i=0}^{k-1}pmatrix{k-1\i}x^i\ &=k(1+x)^{k-1} end{aligned} ] 所以答案为
[egin{aligned} {[x^{n-2}]}prod_{i=1}^{n}F_{a_i}(x)&=prod_{i=1}^{n}a_i imes [x^{n-2}]prod_{i=1}^{n}(1+x)^{a_i-1}\ &=prod_{i=1}^{n}a_i imes [x^{n-2}](1+x)^{sumlimits_{i=1}^{n}(a_i-1)}\ &=prod_{i=1}^{n}a_i imes pmatrix{sumlimits_{i=1}^{n}(a_i-1)\n-2}(n-2)!\ &=prod_{i=1}^{n}a_i(sum_{i=1}^{n}(a_i-1))^{underline{n-2}} end{aligned} ] 可以做到 (mathcal{O}(n)) 。
还有一种不用生成函数,而是使用构造的方法。我们假设每个点都有一个关键接口,剩下的都是普通接口。我们可以每次执行以下的步骤来生成一棵树。
- 在所有的普通接口中选出来一个
- 在所有的关键接口中选出来一个未使用的且与之前选择的普通接口未连通的关键接口
- 连接这两个接口
执行 (n-2) 次操作以后,我们直接连接剩下的两个关键接口,此时必然可以生成一棵树。我们来考虑怎么计算它的贡献。假设操作从 (0) 编号,那么准备第 (k) 次操作时的普通接口个数为 (sumlimits_{i=1}^{n}(a_i-1)-k) ,关键接口的个数为 (n-k-1) 。我们在一切步骤开始之前需要枚举关键节点,这个的贡献为 (prodlimits_{i=1}^{n}a_i) 。除此之外,由于边本来是无序的,而我们这么做相当于强行使边有序的加入了,所以要除以 ((n-1)!) 。所以总答案为
[egin{aligned} &frac{1}{(n-1)!}prod_{i=1}^{n}a_iprod_{i=0}^{n-3}(n-i-1)(sumlimits_{j=1}^{n}(a_j-1)-i)\ =&frac{1}{(n-1)!}prod_{i=1}^{n}a_iprod_{i=0}^{n-3}(n-i-1)prod_{i=0}^{n-3}(sum_{j=1}^{n}(a_j-1)-i)\ =&frac{1}{(n-1)!}prod_{i=1}^{n}a_i(n-1)!(sum_{i=1}^{n}(a_i-1))^{underline{n-2}}\ =&prod_{i=1}^{n}a_i(sum_{i=1}^{n}(a_i-1))^{underline{n-2}} end{aligned} ] 得到的式子与之前的一致。【鼓掌】
-
代码:
const int N=2e5+5,Mod=998244353; using namespace std; int n,sum,ans=1; int main(){ r(n); for(int i=1,d;i<=n;++i) r(d),ans=1ll*ans*d%Mod,sum=(sum+d-1)%Mod; for(int i=0;i<n-2;++i) ans=1ll*ans*(sum+Mod-i)%Mod; w(ans); return 0; }