T1:bzoj4711:小奇挖矿
题意:喵星系有n个星球,标号为1到n,星球以及星球间的航线形成一棵树。所有星球间的双向航线的长度都为1。小奇要在若干个星球建矿石仓库,设立每个仓库的费用为K。对于未设立矿石仓库的星球,设其到一个仓库的距离为i,则将矿石运回的费用为Di。请你帮它决策最小化费用。
思路:设f[i][j]表示以i为根的子树仓库设在j的最小代价,然后设在j的仓库的花费不考虑进f[i][j]。首先处理出dis[i][j]表示i到j的距离,然后f[i][j]初值为d[dis[i][j]],然后因为它的儿子的仓库可能和它的仓库不在一起,如果仓库设在以i为根的子树外,那么显然儿子节点可以与它共用同一个仓库,但如果仓库设在子树内,儿子节点就可能和它并不共用同一个仓库了,那么就对儿子节点求出minval代表仓库设在子树内的最小代价,然后转移就是minval=min(minval,f[son[i]][j]+k);f[i][j]+=min(minval,f[son[i]][j]);然后注意的就是因为状态设的是没有将建残酷的成本算在内,因而更新minval是新建一个仓库的代价,要加上k,更新f[i][j]时f[son[i]][j]不要加上k因为这时是和f[i][j]共用一个仓库,这个时候建仓库的钱会在更新f[fa[i]][j]时加上,因此最终答案也是f[1][i]+k。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 #define maxn 205 8 #define inf 0x7fffffff 9 10 int n,K,tot,t,tt=inf; 11 int d[maxn],now[maxn],pre[2*maxn],son[2*maxn],dep[maxn]; 12 int dis[maxn][maxn],f[maxn][maxn]; 13 14 void add(int a,int b){ 15 son[++tot]=b; 16 pre[tot]=now[a]; 17 now[a]=tot; 18 } 19 20 void link(int a,int b){ 21 add(a,b),add(b,a); 22 } 23 24 void dfs(int t,int x,int fa,int dist){ 25 for (int p=now[x];p;p=pre[p]) 26 if (son[p]!=fa) dis[t][son[p]]=dist,dfs(t,son[p],x,dist+1); 27 } 28 29 void getdep(int x,int fa){ 30 dep[x]=dep[fa]+1; 31 for (int p=now[x];p;p=pre[p]) 32 if (son[p]!=fa) getdep(son[p],x); 33 } 34 35 void tree_dp(int x,int fa){ 36 for (int i=1;i<=n;i++) f[x][i]=d[dis[x][i]]; 37 for (int p=now[x];p;p=pre[p]) 38 if (son[p]!=fa){ 39 tree_dp(son[p],x);int tmp=inf; 40 for (int i=1;i<=n;i++) if (dep[son[p]]<=dep[i]) tmp=min(tmp,f[son[p]][i]+K); 41 for (int i=1;i<=n;i++) f[x][i]+=min(tmp,f[son[p]][i]); 42 } 43 } 44 45 int main(){ 46 scanf("%d%d",&n,&K); 47 for (int i=1;i<n;i++) scanf("%d",&d[i]); 48 for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),link(x,y); 49 for (int i=1;i<=n;i++) dfs(i,i,0,1); 50 getdep(1,0); 51 tree_dp(1,0);int ans=inf; 52 for (int i=1;i<=n;i++) ans=min(ans,f[1][i]+K); 53 printf("%d ",ans); 54 return 0; 55 }
T2:bzoj1547:周末晚会
题意:rena和Sirup正准备下个周末的Party。为这个Party,他们刚刚买了一个非常大的圆桌。他们想邀请每个人,但他们现在不知道如何分配座次。Irena说当有超过K个女孩座位相邻(即这些女孩的座位是连续的,中间没有男孩)的话,她们就会说一整晚的话而不和其他人聊天。 Sirup没有其他选择,只有同意她。然而,作为一名数学家,他很快地痴迷于所有可能方案。 题目说明: N个人围绕着圆桌坐着,其中一些是男孩,另一些是女孩。你的任务是找出所有合法的方案数,使得不超过K个女孩座位是连续的。 循环同构会被认为是同一种方案。
思路:考虑利用Burnside引理,因为对于旋转i格,一共有gcd(n,i)个置换,然后这些置换都是相邻的,相当于只考虑长为gcd(n,i)的一段,然后其余部分均可以通过旋转得到,然后只需要考虑gcd(n,i)中合法方案数即可得到所有本质不同的方案数。设f[j]表示长为j的一段包含所有的男孩且首尾都是男孩且内部不超过k个女孩的方案数。那么这样的话,对于长为gcd(n,i)的一段,用f[j]记录其中包含所有男孩的一段的方案数,其余部分则全是女孩,又由于其余部分的女孩也不能超过k个,因此j应从gcd(n,i)-k枚举到gcd(n,i)即可,那么f[j]对答案的贡献即f[j]*(gcd(n,i)-j+1)(因为只确定了长度,并没有确定位置,然后一共有gcd(n,i)-j+1个位置随意放),接着考虑如何求f[i],可以进行dp,考虑长为i的区间内倒数第二个男孩子出现的位置,可以是i-1,i-2...如果倒数第二个男孩子出现的位置为i-j,那么前面除去最后一个男孩子一定有j-1个女孩子,又因为要合法,因此能也只能枚举到i-k-1,于是f[i]=sigma(f[i-j],1<=j<=k+1),然后可能会被卡成O(n*logn*k*t)有可能会T,因此可以考虑前缀和优化,sum1[i]表示f[i]的前缀和,sum2[i]表示f[i]*i的前缀和,然后就可以做到O(nlogn)计算,O(1)更新答案了。
PS:考场上想到了如上的O(nlogn*t)的做法,但之前写的复杂度还多了一个k,也就是没有用前缀和优化的算法,然后虚的要死,结果加了个前缀和,因为模意义下的减法要加p,结果忘了,又没来得及对拍。。。。活活掉了80,然后交一发复杂度多了一个k的,结果不仅没WA结果还跑得更快。。。当时我就。。。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 #define maxn 2020 8 const int p=100000007; 9 10 int n,k,cases,ans; 11 int f[maxn],sum1[maxn],sum2[maxn]; 12 13 int gcd(int a,int b){ 14 return b==0?a:gcd(b,a%b); 15 } 16 17 int power(int a,int k){ 18 int x=a;a=1; 19 for (;k;k>>=1,x=1ll*x*x%p) if (k&1) a=1ll*a*x%p; 20 return a; 21 } 22 23 int main(){ 24 scanf("%d",&cases); 25 while (cases--){ 26 scanf("%d%d",&n,&k);ans=0;memset(sum1,0,sizeof(sum1)),memset(sum2,0,sizeof(sum2)); 27 if (!k && n){puts("1");continue;} 28 if (n<=k){ 29 for (int i=1;i<=n;i++) ans=(ans+power(2,gcd(n,i)))%p; 30 printf("%d ",1ll*ans*power(n,p-2)%p);continue; 31 } 32 int tt=2;f[1]=1,f[2]=1; 33 for (int i=3;i<=n;i++){ 34 f[i]=tt; 35 if (i-1>k) tt=(tt-f[i-k-1]+p)%p; 36 tt=(tt+f[i])%p; 37 } 38 for (int i=1;i<=n;i++){ 39 int len=gcd(n,i); 40 if (len<=k+1) ans=(ans+power(2,len)-1)%p; 41 else for (int j=len-k;j<=len;j++) ans=(ans+1ll*f[j]*(len-j+1)%p)%p; 42 } 43 ans=1ll*ans*power(n,p-2)%p; 44 printf("%d ",ans); 45 } 46 return 0; 47 }
T3:bzoj4699:树上的最短路
题意:下水道的主干路由n个节点和n-l条边组成,每条边有一个通过它所需的时间Ti。换言之,这是一棵n个节点的带权树。现在,要用最快的速度赶往目标节点k。下水道有一些塌陷,这导致主干路的某一段路径可以通过该塌陷到另一条路径。对于一个塌陷,我们用(L1,ri,L2,R2,c)来描述,即对于主干路上L1到R1路径上的任意节点x,L2到r2路径上的任意节点y,都可以在c的时间内从x走到y。因为不知道自己所在的到底是哪个节点,所以要求出每个节点到目标节点K的最短距离。注意边是单向的。
思路:两遍树剖建出两棵类似于线段树其实就是线段树的东西,只不过之间还要连边而已,第一棵儿子连父亲,第二棵父亲连儿子,对应的叶子节点也同样连边,都是权值为0的边,然后两棵树每一棵树的叶子节点之间按照题意去连边,边权给多少就是多少,最后树剖,如果路径a到b上的点能到c到d的点,因为答案是到目标节点k的距离,相当于是反着连边跑最短路,因此是用第一棵线段树上区间[c,d]代表的点向一个新建节点连权值为给定权值的边,然后再由该点向第二棵线段树上区间[a,b]代表的点连权值为0的边,最后在第一棵线段树上位于位置k的叶子节点跑dijkstra,最后输出第二棵线段树的叶子节点的dis即可。
这样相当于是减少了边数,因为第一棵线段树是儿子连父亲,相当于区间[l,r]的点都能到达不超过logn个点,第二棵线段树是父亲连儿子,相当于只用logn个点就可到达区间[l,r]的所有点,其实就是将原图分成两半,每个点裂成两个,原图中的边还是照样连,然后路径中的边用线段树去限制边数从第一个线段树到第二个线段树去更新即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #include<cmath> 7 using namespace std; 8 #define maxn 250005 9 10 int n,m,s,tot,treedeg; 11 int now[maxn*10],pre[70*maxn],son[70*maxn],val[70*maxn],dep[maxn],heavy[maxn],size[maxn]; 12 int fa[maxn],top[maxn],dfn[maxn],q[maxn*10]; 13 long long dis[maxn*10]; 14 bool vis[maxn*10]; 15 16 struct node{ 17 int pos;long long val; 18 node(){} 19 node(int a,long long b){pos=a,val=b;} 20 bool operator <(const node &a)const{return val>a.val;} 21 }; 22 priority_queue<node> heap; 23 24 int read(){ 25 int x=0;char ch=getchar(); 26 for(;ch<'0'||ch>'9';ch=getchar()); 27 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; 28 return x; 29 } 30 31 struct edge{ 32 int from,to,val; 33 }e[maxn*2]; 34 35 void add(int a,int b,int c){ 36 son[++tot]=b; 37 pre[tot]=now[a]; 38 now[a]=tot; 39 val[tot]=c; 40 } 41 42 void link(int a,int b,int c){ 43 add(a,b,c),add(b,a,c); 44 } 45 46 void getsize(int x){ 47 size[x]=1,dep[x]=dep[fa[x]]+1; 48 for (int p=now[x];p;p=pre[p]) 49 if (son[p]!=fa[x]){ 50 fa[son[p]]=x; 51 getsize(son[p]); 52 size[x]+=size[son[p]]; 53 if (size[heavy[x]]<size[son[p]]) heavy[x]=son[p]; 54 } 55 } 56 57 void getdfn(int x){ 58 dfn[heavy[x]]=dfn[x]+1,top[heavy[x]]=top[x];int cnt=size[heavy[x]]+dfn[heavy[x]]; 59 for (int p=now[x];p;p=pre[p]) 60 if (son[p]!=fa[x]){ 61 if (son[p]!=heavy[x]) dfn[son[p]]=cnt,cnt+=size[son[p]]; 62 getdfn(son[p]); 63 } 64 } 65 66 struct segment_tree{ 67 int root; 68 struct treenode{ 69 int ls,rs; 70 }tree[10*maxn]; 71 void build(int &p,int l,int r,int bo){ 72 if (!p) p=++treedeg; 73 if (l==r) return; 74 int mid=(l+r)>>1; 75 build(tree[p].ls,l,mid,bo);build(tree[p].rs,mid+1,r,bo); 76 if (!bo) add(tree[p].ls,p,0),add(tree[p].rs,p,0); 77 else add(p,tree[p].ls,0),add(p,tree[p].rs,0); 78 } 79 int find(int p,int l,int r,int pos){ 80 if (l==r) return p; 81 int mid=(l+r)>>1; 82 if (pos<=mid) return find(tree[p].ls,l,mid,pos); 83 else return find(tree[p].rs,mid+1,r,pos); 84 } 85 void query(int p,int l,int r,int x,int y,int bo,int val){ 86 if (x<=l && r<=y){ 87 if (bo) add(p,treedeg,val);else add(treedeg,p,val); 88 return; 89 } 90 int mid=(l+r)>>1; 91 if (x<=mid) query(tree[p].ls,l,mid,x,y,bo,val); 92 if (y>mid) query(tree[p].rs,mid+1,r,x,y,bo,val); 93 } 94 }T1,T2; 95 96 void query(int a,int b,int c,int d,int val){ 97 treedeg++;int x=a,y=b; 98 while (top[x]!=top[y]){ 99 if (dep[top[x]]<dep[top[y]]) swap(x,y); 100 T2.query(T2.root,1,n,dfn[top[x]],dfn[x],0,0); 101 x=fa[top[x]]; 102 } 103 if (dep[x]>dep[y]) swap(x,y); 104 T2.query(T2.root,1,n,dfn[x],dfn[y],0,0); 105 x=c,y=d; 106 while (top[x]!=top[y]){ 107 if (dep[top[x]]<dep[top[y]]) swap(x,y); 108 T1.query(T1.root,1,n,dfn[top[x]],dfn[x],1,val); 109 x=fa[top[x]]; 110 } 111 if (dep[x]>dep[y]) swap(x,y); 112 T1.query(T1.root,1,n,dfn[x],dfn[y],1,val); 113 } 114 115 void dijkstra(int x){ 116 memset(dis,127,sizeof(dis)),dis[x]=0; 117 heap.push(node(x,0)); 118 while (!heap.empty()){ 119 node x=heap.top();heap.pop();int pos=x.pos;long long v=x.val; 120 if (vis[pos]) continue;vis[pos]=1; 121 for (int p=now[pos];p;p=pre[p]) 122 if (dis[son[p]]>v+val[p]) heap.push(node(son[p],dis[son[p]]=v+val[p])); 123 } 124 } 125 126 int main(){ 127 n=read(),m=read(),s=read(); 128 for (int i=1,a,b,c;i<n;i++) a=read(),b=read(),c=read(),link(e[i].from=a,e[i].to=b,e[i].val=c); 129 getsize(1);for (int i=1;i<=n;i++) top[i]=i;getdfn(dfn[1]=1); 130 tot=0,memset(now,0,sizeof(now));T1.build(T1.root,1,n,0),T2.build(T2.root,1,n,1); 131 for (int i=1;i<n;i++){ 132 link(T1.find(T1.root,1,n,dfn[e[i].from]),T1.find(T1.root,1,n,dfn[e[i].to]),e[i].val); 133 link(T2.find(T2.root,1,n,dfn[e[i].from]),T2.find(T2.root,1,n,dfn[e[i].to]),e[i].val); 134 } 135 for (int i=1;i<=n;i++) 136 link(T1.find(T1.root,1,n,dfn[i]),T2.find(T2.root,1,n,dfn[i]),0); 137 for (int i=1,a,b,c,d,e;i<=m;i++){ 138 a=read(),b=read(),c=read(),d=read(),e=read(); 139 query(a,b,c,d,e); 140 } 141 dijkstra(T1.find(T1.root,1,n,dfn[s])); 142 for (int i=1;i<=n;i++) printf("%lld ",dis[T2.find(T2.root,1,n,dfn[i])]); 143 return 0; 144 }