hdu4812 D Tree 点分治
这里要求输出字典序最小的两个点,就不能像之前那样容斥了,只能直接搞了。
直接搞的话,需要避开n^2,由于这里是等式,显然应该考虑hash映射。从前往后依次枚举计算每棵子树,对于每个子树结点,快速从前面已经计算过的子树中找到答案更新就可以了。
很简单的东西,只是难以用文字解释得清楚。大概一般点分治不用容斥直接搞大多是这样干吧。
#pragma comment(linker,"/STACK:102400000,102400000") #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<vector> #include<map> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=1000100; const int INF=1e9+10; template<class T> inline void scan(T &ret) { char c=getchar(); while(!isdigit(c)) c=getchar(); ret=c-'0'; while(isdigit(c=getchar())) ret=ret*10+c-'0'; } inline void out(int a) { if(a>9) out(a/10); putchar(a%10+'0'); } const ll MOD=1e6+3; int N,K; int val[maxn]; //vector<int> G[maxn]; int u,v; int ans1,ans2; int id[maxn]; int d[maxn],dn; ll s[maxn]; bool vis[maxn]; int rt,balance; int Inv[maxn]; int e[maxn],tot; int first[maxn],Next[maxn]; void Init() { tot=0; memset(first,-1,sizeof(first)); } void addedge(int u,int v) { e[++tot]=v; Next[tot]=first[u]; first[u]=tot; } ll mul(ll a,ll b,ll MOD) { return (a*b)%MOD; } ll qpow(ll n,ll k,ll p) { ll res=1; while(k){ if(k&1) res=(res*n)%p; n=(n*n)%p; k>>=1; } return res; } void dfs_d(int u,int f,ll dep) { d[++dn]=u; s[u]=dep; //for(int i=0;i<G[u].size();i++){ for(int p=first[u];~p;p=Next[p]){ int v=e[p]; if(v==f||vis[v]) continue; dfs_d(v,u,mul(dep,val[v],MOD)); } } ll inv(ll a,ll p) { return qpow(a,p-2,p); } void update(int x,int y) { if(x>y) swap(x,y); if(x<ans1) ans1=x,ans2=y; else if(x==ans1){ if(y<ans2) ans1=x,ans2=y; } } int get_rt(int u,int f,int sz) { int cnt=1,balance1=0; for(int p=first[u];~p;p=Next[p]){ int v=e[p]; if(v==f||vis[v]) continue; int tmp=get_rt(v,u,sz); cnt+=tmp; balance1=max(balance1,tmp); } balance1=max(balance1,sz-cnt); if(balance1<balance){ balance=balance1; rt=u; } return cnt; } void cls_id(int u,int f) { id[s[u]]=0; //for(int i=0;i<G[u].size();i++){ for(int p=first[u];~p;p=Next[p]){ int v=e[p]; if(v==f||vis[v]) continue; cls_id(v,u); } } void solve(int u) { rt=u,balance=INF; int sz=get_rt(u,0,N); rt=u,balance=INF; get_rt(u,0,sz); u=rt; //cout<<"u="<<u<<endl; vis[u]=1; id[val[u]]=u; s[u]=val[u]; //for(int i=0;i<G[u].size();i++){ for(int p=first[u];~p;p=Next[p]){ int v=e[p]; if(vis[v]) continue; dn=0; dfs_d(v,u,mul(val[u],val[v],MOD)); //cout<<" v="<<v<<" dn="<<dn<<endl; //REP(j,1,dn) cout<<d[j]<<"_";cout<<endl; int idx,x,idy; REP(j,1,dn){ idx=d[j],x=s[idx]; idy=id[mul(mul(K,val[u],MOD),Inv[x],MOD)]; if(idy) update(idx,idy); } REP(j,1,dn){ idx=d[j],x=s[idx]; if(id[x]) id[x]=min(id[x],idx); else id[x]=idx; } } cls_id(u,0); //for(int i=0;i<G[u].size();i++){ for(int p=first[u];~p;p=Next[p]){ int v=e[p]; if(vis[v]) continue; solve(v); } } void play() { freopen("in.txt","w",stdout); int n=10000,k=120; cout<<n<<" "<<k<<endl; REP(i,1,n) cout<<i<<" ";cout<<endl; REP(i,1,n-1){ cout<<i<<" "<<i+1<<endl; } } void Init_inv() { REP(i,0,MOD-1) Inv[i]=inv(i,MOD); } int main() { //play();return 0; freopen("in.txt","r",stdin); Init_inv(); while(~scanf("%d%d",&N,&K)){ Init(); REP(i,1,N) scan(val[i]),val[i]%=MOD; REP(i,1,N-1){ //scanf("%d%d",&u,&v); scan(u);scan(v); addedge(u,v); addedge(v,u); } ans1=ans2=INF; MS0(vis);MS0(id); solve(1); if(ans1==INF) puts("No solution"); else{ //printf("%d %d\n",ans1,ans2); out(ans1); putchar(' '); out(ans2); putchar('\n'); } } return 0; }
TLE了几次,以为是算法问题,看标程都加了输入输出挂,我也加了试交一下,过了。。。是在下输了。。。
没有AC不了的题,只有不努力的ACMER!