ICPC2016大连
EASY:ADEFHIJK
D:$T$次询问$a,b$,求$X,Y$使得$X+Y=a,lcm(X,Y)=b$。$T\leq120000,a\leq20000,b\leq10^9$。
枚举$b$的约数,暴力判断即可。时间复杂度$O(T\frac{\sqrt{b}}{lnb})$。可能不太科学,提供个优秀点的方法,$b$较大时可行的$X$不多,直接枚举。
F:$T$次给出$x$,将$x$拆成若干不同的数,使它们乘积最大。$T\leq100000,x\leq10^9$。
贪心地先拆成$2,3,4,...$,多余的先给后面的数加。特殊地,如果加完还正好多$1$,再给最后一个数加一遍。由于数据组数较多,不能直接模拟,直接推公式求出最大的数即可。
K:很明显的DP,而如果只求答案直接正着扫一遍就好了。要求方案数双指针扫一下范围区间,再利用一个前缀和就能求方案了。
MID:G
G:一个$n$个节点的树,每个节点是$k$种颜色之一。问树上有多少条包含所有颜色的路径。$n\leq50000,k\leq10$。
树分治一下,将$k$种颜色状压,也就是求树上或运算为$2^k-1$的路径条数,那么树分治下去时直接暴力做的复杂度是$O(nlgn2^k)$。
#include<bits/stdc++.h> #define N 55555 #define LL long long #define CL(a) memset(a,0,sizeof a) using namespace std; int n,k,i,x,y,tot,fir[N],ne[N*2],la[N*2],c[N],sz[N],mv[N],V[1024]; LL an; bool vis[N]; void ins(int x,int y){la[++tot]=y;ne[tot]=fir[x];fir[x]=tot;} void dsz(int x,int fa){ sz[x]=1;mv[x]=0; for(int i=fir[x],y;i;i=ne[i]) if(!vis[y=la[i]]&&y!=fa) dsz(y,x),sz[x]+=sz[y],mv[x]=max(mv[x],sz[y]); } void drt(int zs,int x,int fa,int&rt){ mv[x]=max(mv[x],sz[zs]-sz[x]); if(mv[x]<mv[rt]||!rt)rt=x; for(int i=fir[x],y;i;i=ne[i]) if(!vis[y=la[i]]&&y!=fa)drt(zs,y,x,rt); } void add(int x,int fa,int z){ z|=(1<<c[x]);V[z]++; for(int i=fir[x],y;i;i=ne[i])if(!vis[y=la[i]]&&y!=fa)add(y,x,z); } void count(int j){ for(int i=j;i;i=(i-1)&j)an+=V[(1<<k)-1-i]; an+=V[(1<<k)-1]; } void calc(int x,int fa,int z){ z|=(1<<c[x]); count(z); for(int i=fir[x],y;i;i=ne[i])if(!vis[y=la[i]]&&y!=fa)calc(y,x,z); } void fz(int x){ int i,p,y,rt=0; dsz(x,0);drt(x,x,0,rt); vis[rt]=1;CL(V);V[0]=1; for(i=fir[rt];i;i=ne[i])if(!vis[y=la[i]])calc(y,rt,1<<c[rt]),add(y,rt,0); for(i=fir[rt];i;i=ne[i])if(!vis[la[i]])fz(la[i]); } int main(){ for(;~scanf("%d%d",&n,&k);){ tot=an=0;CL(fir);CL(vis); for(i=1;i<=n;i++)scanf("%d",&c[i]),c[i]--; for(i=1;i<n;i++)scanf("%d%d",&x,&y),ins(x,y),ins(y,x); fz(1);an*=2; if(k==1)an+=n; printf("%lld\n",an); } }
不可做:BC
B:对每个数字bitset一下,暴力判断时间复杂度$O(\frac{nm}{64})$。但输入输出爆炸
C:威佐夫博弈,要实数高精度,不会写JAVA