2016-2017 ACM-ICPC Asia-Bangkok Regional Contest
B:数学公式
先直接毙掉DP打表,然后就是数学题了,容斥加隔板法。推出公式:将n个球放入m给盒子,每个盒子最多p个的公式是
why是这样?不知道啊!!然后注意这个公式组合数的参数是可以为负数的,特判一下。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cstdlib> using namespace std; const long long mod=1e9+7; long long fac[12010],rev[12010]; long long qp(long long p,long long q) { int cnt=1; while(q>0) { if(q%2==1) cnt=(cnt*p)%mod; q/=2; p=(p*p)%mod; } return cnt%mod; } long long C(long long a,long long b) { //cout<<a<<" "<<b<<endl; if(a<0||b<0) return 0; long long flag=fac[a]; return ((fac[a]*rev[b])%mod*rev[a-b])%mod; } long long n,k; long long solve(long long sum,long long num) { if(sum==0) return 1; long long cnt=0; int i,j; for(i=0;i<=num;i++) { long long a=C(num,i),b=C(sum-k*i-i+num-1,sum-k*i-i); long long flag=(C(num,i)*C(sum-k*i-i+num-1,sum-k*i-i))%mod; //cout<<flag<<endl; if(i%2==1) flag=(mod-flag)%mod; cnt=(cnt+flag)%mod; } return cnt; } int main() { int i,j; fac[0]=1; rev[0]=1; for(i=1;i<=12005;i++) { fac[i]=(fac[i-1]*i)%mod; rev[i]=qp(fac[i],mod-2); //if(i<=5)cout<<fac[i]<<" "<<rev[i]<<endl; } k=1; while(scanf("%lld%lld",&n,&k)) { if(n==0&&k==0) break; long long ans=0; //cout<<solve(2,2)<<endl; for(i=0;i<=k;i++) { ans=(ans+(solve((n-1)*i,n-1))%mod)%mod; } printf("%lld\n",(ans*n)%mod); } return 0; }
E:dfs序+主席树
按边权跑一遍dfs序得到数组a[i],将这个数组用主席树维护:设T[i]为在dfs序中标号为i的主席树的根,若a[i]>0,则在位置a[i]进行+1,否则-1.
对于一条树链x~y,由dfs序可得这条树链在主席树上可以用T[x]+T[y]-2*T[lca(x,y)]来表示,故查询时分情况查询第k大即可获得中位数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<map> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 7 #define maxn 200000+5 8 9 using namespace std; 10 11 struct Ha_Tree{ 12 int l,r; 13 int sum; 14 }tr[maxn<<5]; 15 16 struct Edge{ 17 int u,v,c,nxt; 18 }e[maxn<<1]; 19 20 int a[maxn],root[maxn],head[maxn],dep[maxn],fa[maxn][20],pos[maxn]; 21 int q,n,len,ind,id,k; 22 23 void addedge(int x,int y,int c){ 24 e[id]=(Edge){x,y,c,head[x]},head[x]=id++; 25 e[id]=(Edge){y,x,c,head[y]},head[y]=id++; 26 } 27 28 void Build(int l,int r,int &rt){ 29 rt=++ind; 30 tr[rt].sum=0; 31 if(l==r) return; 32 int mid=(l+r)>>1; 33 Build(l,mid,tr[rt].l); 34 Build(mid+1,r,tr[rt].r); 35 } 36 37 void Update(int l,int r,int last,int &k,int pos,int val){ 38 k=++ind; 39 tr[k]=tr[last]; tr[k].sum+=val; 40 if(l==r) return; 41 int mid=(l+r)>>1; 42 if(pos<=mid) Update(l,mid,tr[last].l,tr[k].l,pos,val); 43 else Update(mid+1,r,tr[last].r,tr[k].r,pos,val); 44 } 45 46 int Query(int l,int r,int s,int t,int lca,int k){ 47 if(l==r) return r; 48 int mid=(l+r)>>1; 49 int cnt=tr[tr[t].l].sum+tr[tr[s].l].sum-2*tr[tr[lca].l].sum; 50 if(k<=cnt) return Query(l,mid,tr[s].l,tr[t].l,tr[lca].l,k); 51 else return Query(mid+1,r,tr[s].r,tr[t].r,tr[lca].r,k-cnt); 52 } 53 54 void dfs(int x,int p){ 55 dep[x]=dep[p]+1; 56 for(int i=head[x];i!=-1;i=e[i].nxt) 57 if(e[i].v!=p){ 58 a[++len]=e[i].c; 59 fa[e[i].v][0]=x; 60 pos[e[i].v]=len; 61 62 dfs(e[i].v,x); 63 64 a[++len]=-e[i].c; 65 } 66 } 67 68 void init(){ 69 for(int i=1;i<20;i++) 70 for(int j=1;j<=n;j++) 71 fa[j][i]=fa[fa[j][i-1]][i-1]; 72 } 73 74 int LCA(int x,int y){ 75 if(dep[x]>dep[y]) swap(x,y); 76 for(int i=19;i>=0;i--) 77 if((dep[y]-dep[x])&(1<<i)) y=fa[y][i]; 78 if(x==y) return x; 79 for(int i=19;i>=0;i--) 80 if(fa[x][i]!=fa[y][i]){ 81 x=fa[x][i]; y=fa[y][i]; 82 } 83 return fa[x][0]; 84 } 85 86 int da(int x,int s){ 87 for(int i=19;i>=0;i--) 88 if(s&(1<<i)) x=fa[x][i]; 89 return x; 90 } 91 92 int main(){ 93 int T; 94 scanf("%d",&T); 95 while(T--){ 96 int lim=0; 97 scanf("%d",&n); 98 id=len=ind=0; 99 memset(a,0,sizeof(a)); memset(tr,0,sizeof(tr)); 100 memset(root,0,sizeof(root)); 101 memset(head,-1,sizeof(head)); 102 for(int i=1;i<n;i++){ 103 int x,y,c; 104 scanf("%d%d%d",&x,&y,&c); 105 addedge(x,y,c); lim=max(lim,c); 106 } 107 a[++len]=0; pos[1]=1; 108 dfs(1,0); init(); 109 Build(1,lim,root[0]); 110 for(int i=1;i<=len;i++){ 111 if(a[i]>0) Update(1,lim,root[i-1],root[i],a[i],1); 112 else if(a[i]<0) Update(1,lim,root[i-1],root[i],-a[i],-1); 113 else Update(1,lim,root[i-1],root[i],1,0); 114 } 115 scanf("%d",&q); 116 for(int i=1;i<=q;i++){ 117 int x,y,l,sum; 118 int rtx,rty,rtl; 119 scanf("%d%d",&x,&y); 120 l=LCA(x,y); 121 sum=dep[x]+dep[y]-2*dep[l]; 122 rtx=root[pos[x]]; rty=root[pos[y]]; rtl=root[pos[l]]; 123 if(sum%2==0){ 124 printf("%.1f\n",(Query(1,lim,rtx,rty,rtl,sum/2)+Query(1,lim,rtx,rty,rtl,sum/2+1))*0.5); 125 } 126 else{ 127 printf("%.1f\n",Query(1,lim,rtx,rty,rtl,sum/2+1)*1.0); 128 } 129 130 } 131 } 132 return 0; 133 }
F:(Trie树+树上博弈)
树上删边游戏博弈结论:叶子节点的SG值为0,非叶子节点的SG值为子节点SG值+1的异或和。
每次加入一个字符串便在Trie树上扩展一条链,更新链上节点的SG值(注意消除这条链上之前节点的影响)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
G:矩阵快速幂
首先找规律,发现数量规律是一个斐波拉契数列,长度为k的串,长度为f(k+1)。之后求[L,R]区间内的和,于是可以想到利用矩阵快速幂求前缀和,将2*2的斐波拉契数列系数矩阵增加一维求和。注意初始的是f(k+1)。所以构造矩阵有一些奇特(见代码)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstdlib> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <vector> #include <string> #include <map> using namespace std; const long long mod = 1e9 + 7; struct Matrix { long long data[35][35]; int col, row; Matrix() {} Matrix(int _row, int _col) { row = _row; col = _col; memset(data, 0, sizeof(data)); } const long long * operator [] (int row) const //жидиЯТБъдЫЫуЗћ { return data[row]; } long long * operator [] (int row) { return data[row]; } }; Matrix operator * (const Matrix &m1, const Matrix &m2) { Matrix ans_met(m1.row, m2.col); int i, j, k; for (i = 0; i < m1.row; i++) { for (j = 0; j < m2.col; j++) { for (k = 0; k < m1.col; k++) { ans_met[i][j] = (ans_met[i][j] + (m1[i][k] * m2[k][j]) % mod) % mod; } } } return ans_met; } Matrix operator ^(const Matrix &mm, long long q) { Matrix ans_met(mm.row, mm.col); Matrix ret(mm.row, mm.col); int i, j; for (i = 0; i < ans_met.row; i++) { for (j = 0; j < ans_met.col; j++) ret[i][j] = mm[i][j]; ans_met[i][i] = 1; } while (q > 0) { if (q % 2 == 1) ans_met = ans_met * ret; ret = ret * ret; q /= 2; } return ans_met; } Matrix operator + (const Matrix &m1, const Matrix &m2) { Matrix ans_met(m1.row, m2.col); int i, j; for (i = 0; i < m1.row; i++) { for (j = 0; j < m1.col; j++) { ans_met[i][j] = (m1[i][j] + m2[i][j]) % mod; } } return ans_met; } Matrix fab(2, 2), mat(3, 3); long long solve(long long q) { q++; //注意要移位 Matrix tmp(3, 3); tmp = mat ^ q; return tmp[0][2]-1; } int main() { int i, j; int t; scanf("%d", &t); int cases = 1; while (t--) { long long l, r, k; scanf("%lld%lld%lld", &l, &r, &k); if (l%k != 0) l += k - (l%k); //处理余数 if (r%k != 0) r -= r % k; fab[0][0] = 1; fab[0][1] = 1; fab[1][0] = 1; fab[1][1] = 0; fab = fab ^ k; mat[0][0] = fab[0][0]; //构造求和矩阵 mat[0][1] = fab[0][1]; mat[1][0] = fab[1][0]; mat[1][1] = fab[1][1]; mat[0][2] = 1; mat[1][2] = 1; mat[2][0] = 0; mat[2][1] = 0; mat[2][2] = 1; long long le = solve(l / k - 1), ri = solve(r / k); long long ans = ((ri - le) % mod + mod) % mod; printf("Case %d: ", cases++); printf("%d\n", ans); } return 0; }
I:LCA+倍增
问题的关键是确定当前根与查询子树的根节点的关系,确定关系后可以直接在原树中用删减的方式得到新树里待查询子树的大小。具体方式如下:
1.查询点x与当前根rt的lca的深度小于x的深度(rt不在x的子树内):答案为原树中x的子树大小size[x]
2.查询点x与当前根rt的lca为x(rt在x的子树内):将rt倍增至x的儿子t处,答案为n-size[t]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 6 #define maxn 100000+5 7 8 using namespace std; 9 10 struct Edge{ 11 int u,v,nxt; 12 }e[maxn<<1]; 13 14 int head[maxn],fa[maxn][20],dep[maxn],sz[maxn]; 15 int n,m,root,ind; 16 17 void addedge(int x,int y){ 18 e[ind]=(Edge){x,y,head[x]},head[x]=ind++; 19 e[ind]=(Edge){y,x,head[y]},head[y]=ind++; 20 } 21 22 void dfs(int x,int p){ 23 dep[x]=dep[p]+1; sz[x]=1; 24 for(int i=head[x];i!=-1;i=e[i].nxt) 25 if(e[i].v!=p){ 26 fa[e[i].v][0]=x; 27 dfs(e[i].v,x); 28 sz[x]+=sz[e[i].v]; 29 } 30 } 31 32 void init(){ 33 for(int i=1;i<20;i++) 34 for(int j=1;j<=n;j++) 35 fa[j][i]=fa[fa[j][i-1]][i-1]; 36 } 37 38 int LCA(int x,int y){ 39 if(dep[x]>dep[y]) swap(x,y); 40 for(int i=19;i>=0;i--) 41 if((dep[y]-dep[x])&(1<<i)) y=fa[y][i]; 42 if(x==y) return x; 43 for(int i=19;i>=0;i--) 44 if(fa[x][i]!=fa[y][i]){ 45 x=fa[x][i]; y=fa[y][i]; 46 } 47 return fa[x][0]; 48 } 49 50 int da(int x,int s){ 51 for(int i=19;i>=0;i--) 52 if(s&(1<<i)) x=fa[x][i]; 53 return x; 54 } 55 56 int main(){ 57 int T,kase=1; 58 scanf("%d",&T); 59 while(T--){ 60 memset(head,-1,sizeof(head)); 61 memset(fa,0,sizeof(fa)); 62 memset(dep,0,sizeof(dep)); 63 64 printf("Case #%d:\n",kase++); 65 scanf("%d%d%d",&n,&m,&root); ind=0; 66 for(int i=1;i<n;i++){ 67 int x,y; 68 scanf("%d%d",&x,&y); 69 addedge(x,y); 70 } 71 dfs(root,0); 72 init(); 73 for(int i=1;i<=m;i++){ 74 int op,x; 75 scanf("%d%d",&op,&x); 76 if(op==1){ 77 int lca=LCA(x,root); 78 if(dep[x]>dep[lca]) printf("%d\n",sz[x]); 79 else{ 80 int p=da(root,dep[root]-dep[x]-1); 81 printf("%d\n",n-sz[p]); 82 } 83 } 84 else root=x; 85 } 86 } 87 return 0; 88 }
L:模拟+dfs
由于题目保证可以确定所有坐标,故建立双向边构图,dfs一遍后将坐标调整进[-1e9,1e9]区间内即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cctype> 5 #include<algorithm> 6 7 #define maxn 100000+5 8 #define maxm 1000000+5 9 10 using namespace std; 11 12 struct Edge{ 13 int u,v,dx,dy,nxt; 14 }e[maxm<<1]; 15 16 int x[maxn],y[maxn],head[maxn],ok[maxn],du[maxn]; 17 int n,m,ind; 18 19 void addedge(int x,int y,int dx,int dy){ 20 e[ind]=(Edge){x,y,dx,dy,head[x]},head[x]=ind++; 21 e[ind]=(Edge){y,x,-dx,-dy,head[y]},head[y]=ind++; 22 } 23 24 void dfs(int xx,int yy,int p){ 25 ok[p]=1; x[p]=xx; y[p]=yy; 26 for(int i=head[p];i!=-1;i=e[i].nxt) 27 if(!ok[e[i].v]){ 28 dfs(xx+e[i].dx,yy+e[i].dy,e[i].v); 29 } 30 } 31 32 int main(){ 33 scanf("%d%d",&n,&m); 34 memset(head,-1,sizeof(head)); 35 for(int i=1;i<=m;i++){ 36 int a,b,dx,dy; 37 scanf("%d%d%d%d",&a,&b,&dx,&dy); 38 addedge(a,b,dx,dy); 39 } 40 dfs(0,0,1); 41 int mx=INT_MAX,my=INT_MAX,xm=INT_MIN,ym=INT_MIN; 42 for(int i=1;i<=n;i++){ 43 mx=min(x[i],mx); my=min(y[i],my); 44 ym=max(y[i],ym); xm=max(x[i],xm); 45 } 46 if(xm>1e9){ 47 for(int i=1;i<=n;i++){ 48 x[i]-=xm-1e9+1; 49 } 50 } 51 if(ym>1e9){ 52 for(int i=1;i<=n;i++){ 53 y[i]-=ym-1e9+1; 54 } 55 } 56 if(mx<-1e9){ 57 for(int i=1;i<=n;i++){ 58 x[i]+=-1e9-mx+1; 59 } 60 } 61 if(my<-1e9){ 62 for(int i=1;i<=n;i++){ 63 y[i]+=-1e9-my+1; 64 } 65 } 66 for(int i=1;i<=n;i++){ 67 printf("%d %d\n",x[i],y[i]); 68 } 69 return 0; 70 }