Wannafly挑战赛16
https://www.nowcoder.com/acm/contest/113#question
a题
等价于规定4种石子相同种类的绝对顺序,然后石子种类不同间随便你排
因为最后总要排成sum 个 我们 从sum个中去a个按顺序放a就行了 所以答案是c(a,sum)c(b,sum-a)c(c,sum-a-b); 暴力算一算
#include <stdio.h> using namespace std; const int maxn = 1e5+9; const int mod = 1e9+7; typedef long long int ll; ll quick(ll a,ll b){ ll ret = 1; while (b) { if(b&1){ ret*=a; ret%=mod; } a*=a; a%=mod; b>>=1; } return ret; } int main(){ int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d); int sum = a+b+c+d; ll ans = 1; for(int i=0;i<a;i++){ ll now = sum-i; ans*=now; ans%=mod; } for(int i=1;i<=a;i++){ ans*=quick(i,mod-2); ans%=mod; } sum-=a; for(int i=0;i<b;i++){ ll now = sum-i; ans*=now; ans%=mod; } for(int i=1;i<=b;i++){ ans*=quick(i,mod-2); ans%=mod; } sum-=b; for(int i=0;i<c;i++){ ll now = sum-i; ans*=now; ans%=mod; } for(int i=1;i<=c;i++){ ans*=quick(i,mod-2); ans%=mod; } printf("%lld\n",ans); return 0; }
b题
结论就是减去中位数,注意偶数个数的时候的情况,证明参考 糖果传递
#include <stdio.h> #include <algorithm> using namespace std; const int maxn = 2e5+9; const int mod = 1e9+7; typedef long long int ll; int a[maxn]; int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=0;i<n;i++){ scanf("%d",&a[i]); } for(int i=n;i<m+n;i++){ int ttt; scanf("%d",&ttt); a[i] = -ttt; } sort(a,a+m+n); if((m+n)&1){ int bb = a[(m+n)/2]; ll ans = 0; for(int i=0;i<n+m;i++){ ans+=abs(a[i]-bb); } ans+=abs(bb); printf("%lld\n",ans); }else{ int bb = a[(m+n)/2]; ll ans1 = 0; for(int i=0;i<n+m;i++){ ans1+=abs(a[i]-bb); } ans1+=abs(bb); bb = a[(m+n)/2-1]; ll ans2 = 0; for(int i=0;i<n+m;i++){ ans2+=abs(a[i]-bb); } ans2+=abs(bb); printf("%lld\n",min(ans1,ans2)); } return 0; }
c题
l,r会有1的贡献
(l,r/3)会有1的贡献
(l,r/5) 会有1的贡献 所以把这些区间求和处以区间长度就是答案,具体就是预处理奇数项调和级数的前缀和搞搞
#include <stdio.h> #include <algorithm> using namespace std; const int maxn = 5e6+9; const int mod = 998244353; typedef long long int ll; ll kk[maxn]; ll quick(ll a,ll b){ ll ret = 1; while (b) { if(b&1){ ret*=a; ret%=mod; } a*=a; a%=mod; b>>=1; } return ret; } int main(){ for(int i=1;i<maxn;i++){ kk[i] = quick(2*i-1,mod-2); kk[i]+=kk[i-1]; kk[i]%=mod; } int T; scanf("%d",&T); while (T--) { ll l,r; scanf("%lld%lld",&l,&r); ll ii = (r+l)/2/l; ll ans = (((kk[ii]*r)%mod-l*ii)%mod+mod)%mod; ans*=quick(r-l,mod-2); ans%=mod; printf("%lld\n",ans); } return 0; }
d题
基本思路就是把时间转成距离 做最段路
就是有n层 每层a*b个点 每层里每个点之间转移有距离 层与层之间对应点转移有距离
然后我们发现最优解可以一层一层推 然后二维点可以转成一维的转移,因为每行从i列到j列的转移代价一样
这样就成了个dp
我还特傻搞了dij堆优化和spfa明显状态爆炸....还是太菜了
#include <bits/stdc++.h> using namespace std; typedef long long int ll; const int maxn = 109; int a,b,c,n; int xx[maxn]; int yy[maxn]; int ff[maxn][maxn]; int gg[maxn][maxn]; int hh[22][maxn][maxn]; ll dis[maxn][maxn]; int ww[maxn]; int tt[maxn]; void go(int nowceng){ for(int i=1;i<=a;i++){ for(int j=1;j<=b;j++){ for(int k = 1;k<=a;k++){ dis[k][j] = min(dis[k][j],dis[i][j]+ff[i][k]); } } } for(int i=1;i<=a;i++){ for(int j=1;j<=b;j++){ for(int k = 1;k<=b;k++){ dis[i][k] = min(dis[i][k],dis[i][j]+gg[j][k]); } } } for(int i=1;i<=a;i++){ for(int j=1;j<=b;j++){ dis[i][j]+=ww[nowceng+1]/hh[tt[nowceng+1]][i][j]; if(ww[nowceng+1]%hh[tt[nowceng+1]][i][j]!=0) dis[i][j]++; } } } int main() { scanf("%d%d%d%d",&a,&b,&c,&n); /* pp.push(node{10,1,1,1}); pp.push(node{12,1,1,1}); node tmp = pp.top(); cout<<tmp.val<<endl; */ for(int i=1;i<=a;i++){ scanf("%d",&xx[i]); } for(int i=1;i<=b;i++){ scanf("%d",&yy[i]); } for(int i=1;i<=a;i++){ for(int j=1;j<=a;j++){ scanf("%d",&ff[i][j]); } } for(int i=1;i<=b;i++){ for(int j=1;j<=b;j++){ scanf("%d",&gg[i][j]); } } for(int k=1;k<=a;k++){ for(int i=1;i<=a;i++){ for(int j=1;j<=a;j++){ ff[i][j] = min(ff[i][j],ff[i][k]+ff[k][j]); } } } for(int k=1;k<=b;k++){ for(int i=1;i<=b;i++){ for(int j=1;j<=b;j++){ gg[i][j] = min(gg[i][j],gg[i][k]+gg[k][j]); } } } for(int i=1;i<=c;i++){ for(int j=1;j<=a;j++){ for(int k = 1;k<=b;k++){ scanf("%d",&hh[i][j][k]); } } } for(int i=1;i<=n;i++){ scanf("%d",&ww[i]); } for(int i=1;i<=n;i++){ scanf("%d",&tt[i]); } memset(dis,0x3f,sizeof(dis)); for(int i=1;i<=a;i++){ for(int j=1;j<=b;j++){ dis[i][j] = xx[i]+yy[j]; } } for(int i=0;i<n;i++){ go(i); } ll ans= 0x3f3f3f3f3f3f3f3f; for(int i=1;i<=a;i++){ for(int j=1;j<=b;j++){ ans = min(ans,dis[i][j]); } } printf("%lld\n",ans); return 0; } /* 2 2 1 1 1 100 1 100 0 10 5 0 0 7 6 0 2 3 4 5 1001 1 */
e题
核心是距离和时间的等价转换
首先这个图是环套数,先把环处理出来,树处理出来
对换上的点插入只要找到一个基准点转化就行了
然后是对树上的点的操作,我们都需要转化为树根的状态,可是这样会有混淆,不是一个子树的会混在一起,所以我们需要区分,考虑数据结构,用dfs序维护需要查询的子树,所以用一颗可持久化线段树维护,距离加时间作为根的入口,然后维护dfs序,这样查询就是从时间加根入口查询一段区间(子树)的和,插入就是在对应的距离加时间查在dfs序上,注意树上的点总会到环上,要维护一下啥时候会到环上
一开始想的是外面是dfs序,里面是距离加时间,然后发现这样根本不能查询一段dfs序,是不对的
#include <stdio.h> #include <algorithm> #include <cstring> #include <queue> #include <vector> using namespace std; const int maxn = 5e5+9; int a[maxn]; int in[maxn]; int type[maxn]; int deep[maxn],ldfs[maxn],rdfs[maxn]; int bl[maxn]; int ndfs=0; int cirt[maxn]; vector<int> circonv[maxn]; int fcirt[maxn]; int getpos(int now,int val,int tt){ return circonv[ cirt[now] ] [((int)(circonv[ cirt[now] ].size())-fcirt[now]+tt)%(int)(circonv[ cirt[now] ].size())]+=val; } vector<int> todolist[maxn*2]; queue<int> qq; struct EDGE{ int v,nex; }e[maxn]; int tot =0; int head[maxn]; void addedge(int u,int v){ e[tot] = (EDGE){v,head[u]}; head[u] = tot++; } struct Node{ int lson,rson,sum; }node[60*maxn]; int rt[maxn*2]; int nodecnt = 0; int build(int l,int r){ int now = nodecnt++; node[now] = (Node){-1,-1,0}; if(l==r){ return now; } int mid = (l+r)>>1; node[now].lson = build(l,mid); node[now].rson = build(mid+1,r); return now; } void dfs(int now,int dep,int belo){ bl[now] = belo; ldfs[now] = ++ndfs; deep[now] = dep; for(int i=head[now];i!=-1;i=e[i].nex){ int v = e[i].v; dfs(v,dep+1,belo); } rdfs[now] = ndfs; } int insert(int root,int l,int r,int pos){ int now = nodecnt++; node[now] = node[root]; node[now].sum++; if(l==r) return now; int mid = (l+r)>>1; if(pos<=mid) node[now].lson = insert(node[now].lson,l,mid,pos); else node[now].rson = insert(node[now].rson,mid+1,r,pos); return now; } int query(int root,int l,int r,int ql,int qr){ if(ql<=l && r<=qr){ return node[root].sum; } int mid=(l+r)>>1; if(qr<=mid) return query(node[root].lson,l,mid,ql,qr); if(ql>mid) return query(node[root].rson,mid+1,r,ql,qr); return query(node[root].lson,l,mid,ql,qr)+query(node[root].rson,mid+1,r,ql,qr); } int main(){ memset(head,-1,sizeof(head)); int n; scanf("%d",&n); int last = 0; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); in[a[i]]++; addedge(a[i],i); } for(int i=1;i<=n;i++){ if(in[i]==0) { qq.push(i); type[i] = 1; } } while(!qq.empty()){ int tmp = qq.front(); qq.pop(); in[a[tmp]]--; if(in[a[tmp]]==0) { qq.push(a[tmp]); type[a[tmp]] = 1; } } for(int i=1;i<=n;i++){ if(type[i]==0){ int nex = i; int cntt = 0; while(type[nex]==0){ type[nex] = 2; cirt[nex] = i; fcirt[nex] = cntt++; nex = a[nex]; } circonv[i].resize(cntt); } } for(int i=1;i<=n;i++){ if(type[i]==1 &&type[a[i]]==2){ dfs(i,0,a[i]); } } build(1,ndfs); int m; scanf("%d",&m); for(int tt=0;tt<m;tt++){ int now; scanf("%d",&now); now^=last; for(int i=0;i<todolist[tt].size();i++){ int todo = todolist[tt][i]; getpos(todo,1,tt); } if(type[now] == 1){ rt[deep[now] + tt]=insert(rt[deep[now] + tt],1,ndfs,ldfs[now]); last = query(rt[deep[now]+tt],1,ndfs,ldfs[now],rdfs[now]); todolist[deep[now]+tt+1].push_back(bl[now]); }else{ last = getpos(now,1,tt); } printf("%d\n",last); } return 0; }
f留坑