之江学院第0届校赛题解
可以容斥算出报到数字t的时候,需要多少名学生,然后二分这个t就行
算lcm的时候,注意会超过long long ,可以用double判断下,或者__int128(不过好像并没有这样的数据)
qwb与矩阵nmlogm的dp
勤劳的ACgirls用隔板法算出答案是C(n+(1-k)*m-1,m-1),然后算出因子5和因子2的个数,取小的那个
int m,n,r;
while(scanf("%d%d%d",&m,&n,&r)==3){
LL x=m-1,y=n+(LL)(1-r)*m-1;
int ans=0;
if(x>y||y<=0){
ans=0;
}else{
int c2=0,c5=0;
for(LL i=2;i<=y;i=i*2){
c2+=y/i-(y-x)/i-x/i;
}
for(LL i=5;i<=y;i=i*5){
c5+=y/i-(y-x)/i-x/i;
}
ans=min(c2,c5);
}
printf("%d\n",ans);
}
qwb与神奇的序列
用数列知识 容易算出答案是(3^n+1)/2
本题要求mod1e8,2和1e8不互质,没有逆元
易证 A/C%B=A%(B*C)/C
那在快速幂的时候 %2e8就行了
qwb和李主席折半搜索+二分查找,注意可能会因为浮点数误差导致WA,解决方法是一开始*200化成LL,
具体操作就是
double x;
scanf("%lf",&x);
a[i]=round(x*200);
//或者a[i]=x*200+0.1;
也可以直接用long double过。
有些方法可以用double过,反正我的不可以。
qwb has a lot of Coins唯一的英文题一定是个水题,先手只要想办法让异或值等于0就行。
int n;
while(scanf("%d",&n)==1){
int x=0;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
x^=a[i];
}
int ans=0;
if(x!=0)
for(int i=0;i<n;i++){
int y=x^a[i];
if(a[i]>=y)ans++;
}
printf("%d\n",ans);
}
qwb去面试
怕大家爆0,特意加的水题。很容易看出分成2和3最好,并且6=2+2+2=3+3,2*2*2=8,3*3=9,说明尽量分成3.
也可以这样想,如果把问题改成连续的,也就是能分成小数,显然是每个数一样的时候最好,
也就是算 (n/x)^x的最大值,通过求导算出n/x=e的时候有极值,所以要尽量分成3和2
int _;scanf("%d",&_);
while(_--){
int n;scanf("%d",&n);
int ans;
if(n==1){
ans=1;
}else{
if(n%3==0){
ans=pow_mod(3,n/3);
}else if(n%3==1){
ans=pow_mod(3,n/3-1)*4%mod;
}else{
ans=pow_mod(3,n/3)*2%mod;
}
}
printf("%d\n",ans);
}
题目意思是查询图上两点的瓶颈路大小,这个训练指南上有讲解。
先是求最大生成树,然后求树上两点路径的最小值,用LCA倍增法就可以了,
时间复杂度O(mlogm+nlogn+klogn)
template <class T> inline void umin(T &a,T b){a=min(a,b);}
template <class T> inline void umax(T &a,T b){a=max(a,b);}
const int N=50005;
struct Bian{
int u;
int v;
int w;
};
int p[N],ra[N];
Bian bian[200005];
vector<pii> G[N];
bool cmp(const Bian &p1,const Bian &p2){
return p1.w>p2.w;
}
int find(int x){
return p[x]==x?x:p[x]=find(p[x]);
}
int Union(int x,int y) {
x=find(x);
y=find(y);
if(x==y)return 0;
if(ra[x]>ra[y]) {
p[y]=x;
}else{
if(ra[x]==ra[y])ra[y]++;
p[x]=y;
}
return 1;
}
void Kruskal(int kn,int km){
for(int i=1;i<=kn;i++){
p[i]=i;
}
sort(bian,bian+km,cmp);
for(int i=0;i<km;i++){
if(Union(bian[i].u,bian[i].v)){
G[bian[i].u].push_back({bian[i].v,bian[i].w});
G[bian[i].v].push_back({bian[i].u,bian[i].w});
}
}
}
int d[N][20],f[N][20];
int dep[N];
void dfs(int u,int fa){
dep[u]=dep[fa]+1;
for(int i=0;i<G[u].size();i++){
int v=G[u][i].ff;
if(v==fa)continue;
f[v][0]=u;
d[v][0]=G[u][i].ss;
dfs(v,u);
}
}
int main() {
#ifndef ONLINE_JUDGE
//freopen("sb.txt","r",stdin);
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=0;i<m;i++){
scanf("%d%d%d",&bian[i].u,&bian[i].v,&bian[i].w);
}
Kruskal(n,m);
dep[1]=1;
dfs(1,0);
int wc=0;//log深度
for(int i=1;;wc++){
i<<=1;
if(i>n)break;
}
for(int j=1;j<=wc;j++){
for(int i=1;i<=n;i++){
f[i][j]=f[f[i][j-1]][j-1];
d[i][j]=min(d[i][j-1],d[f[i][j-1]][j-1]);
}
}
while(q--){
int x,y;
scanf("%d%d",&x,&y);
int ans=INF;
if(dep[x]<dep[y])swap(x,y);
for(int i=wc;i>=0;i--){
if(dep[f[x][i]]>=dep[y]){
umin(ans,d[x][i]);
x=f[x][i];
if(dep[x]==dep[y])break;
}
}
if(x!=y){
for(int i=wc;i>=0;i--){
if(f[x][i]!=f[y][i]){
umin(ans,d[x][i]);
umin(ans,d[y][i]);
x=f[x][i];
y=f[y][i];
}
}
umin(ans,d[x][0]);
umin(ans,d[y][0]);
}
printf("%d\n",ans);
}
return 0;
}
也可以通过重建树,控制树高为logn,不用倍增法直接暴力lca。
直接在算最大生成树的时候,连接两个并查集的根,因为是从大到小取边的,所以两点间的最小值不会受影响。
注意并查集要安秩合并!
LL mod=1e9+7;
const int N=50005;
struct Bian {
int u;
int v;
int w;
};
int p[N];
Bian bian[200005];
vector<pii> G[N];
bool cmp(const Bian &p1,const Bian &p2){
return p1.w>p2.w;
}
int find(int x) {
return p[x]==x?x:p[x]=find(p[x]);
}
int ra[N];
void Kruskal(int kn,int km) {
for(int i=1; i<=kn; i++) {
p[i]=i;
ra[i]=0;
}
sort(bian,bian+km,cmp);
for(int i=0; i<km; i++) {
int x=bian[i].u,y=bian[i].v;
x=find(x);
y=find(y);
if(x==y)continue;
if(ra[x]>ra[y]) {
p[y]=x;
G[x].push_back({y,bian[i].w});
}else{
if(ra[x]==ra[y])ra[y]++;
G[y].push_back({x,bian[i].w});
p[x]=y;
}
}
}
int d[N],f[N];
int dep[N];
void dfs(int u,int fa) {
dep[u]=dep[fa]+1;
for(int i=0; i<G[u].size(); i++) {
int v=G[u][i].ff;
if(v==fa)continue;
f[v]=u;
d[v]=G[u][i].ss;
dfs(v,u);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("t3.in","r",stdin);
freopen("t3.out","w",stdout);
#endif
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=0; i<m; i++) {
scanf("%d%d%d",&bian[i].u,&bian[i].v,&bian[i].w);
}
Kruskal(n,m);
dep[find(1)]=1;
dfs(find(1),0);
while(q--) {
int x,y;
scanf("%d%d",&x,&y);
int ans=INF;
if(dep[x]<dep[y])swap(x,y);
while(dep[x]>dep[y]) {
umin(ans,d[x]);
x=f[x];
}
while(x!=y) {
umin(ans,d[x]);
umin(ans,d[y]);
x=f[x];
y=f[y];
}
printf("%d\n",ans);
}
return 0;
}
qwb VS 去污棒
原题是BZOJ-3261,就当解放权限题了。
可持久化字典树,我也要去学学。
qwb又偷懒了用两个树状数组分别保存收入和人数,坑点在收入为0的情况,可以记录一下0的个数,或者直接平移1。
qwb与小数a/b的第n位可以由第n-1位求出,第n-1位除的余数乘10再除以b就是第n位的数,答案就是a*pow_mod(10,n-1.b)%b*10/b或者a*pow_mod(10,n,10*b)/b%10。快速幂的时候模取b即可。
by 不愿意透露名字的答疑员水明
我是
ans=(__int128)a*pow_mod(10,n,(__int128)10*b)/b%10;
case=1的时候很容易可以想到一个n^2的暴力
那么在case比较大的时候,把所有case根据n的数值进行排序。再离线处理,枚举每一组(a,b),穷举整数t∈[-20000,20000],当符合条件(a^2+b^2+t)%ab==0时,ans[t]++,最后根据case的顺序从离线处理中获取答案,复杂度O(∑(i=1..n,j=2..n) (40000/ab)) + O(n^1.5)
by ?柠檬
我的方法如下,复杂度比较玄学,900ms...
vector<int> G[N][N];
for(int i=1;i<1000;i++){
for(int j=i+1;j<1000;j++){
int m=i-j*j%i;
if(m==i)m=0;
G[i][m].push_back(j);
}
}
int n,m;
int cas=0;
while(scanf("%d%d",&n,&m),n!=0||m!=0){
int ans=0;
for(int i=1;i<n;i++){
int t=m%i;
if(t<0)t+=i;
for(auto x:G[i][t]){
if(x>=n)break;
if((i*i+x*x+m)%(x*i)==0)ans++;
}
}
printf("Case %d: %d\n",++cas,ans);
}
qwb与二叉树
记忆化搜索暴力过,时间复杂度O(n^4)
LL dp[55][55];
LL dfs(int x,int y){
if(dp[x][y]!=-1)return dp[x][y];
dp[x][y]=0;
//if(y>=x)return dp[x][y]=0;
for(int i=0;i<=x-1;i++){
for(int j=0;j<=i&&j<=y;j++){
dp[x][y]+=dfs(i,j)*dfs(x-1-i,y-j);
dp[x][y]%=mod;
}
}
return dp[x][y];
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int n,m;
memset(dp,-1,sizeof dp);
dp[0][0]=1;
dp[1][0]=0;
dp[1][1]=1;
while(scanf("%d%d",&n,&m)==2){
printf("%lld\n",dfs(n,m));
}
return 0;
}
jnxxhzz的博客 - 博客频道 - CSDN.NET 这个博客里也有本次比赛的题解
相关问题 如何评价之江学院第0届校赛? - 知乎