NAIPC2016部分题解
A题:空
B题:空
C题:状压dp,我们设 dp[i][S] 表示用 i 个信封装集合 S 封信,转移就是 dp[i][S] = min(dp[i][S], dp[i-1][S1] + val[S^S1]),其中 S1是S的子集。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 LL dp[20][1<<15], val[1<<15]; 6 int p[20][3], n, k; 7 8 void input() { 9 scanf("%d%d", &n, &k); 10 for(int i = 0; i < n; ++i) 11 for(int j = 0; j < 3; ++j) 12 scanf("%d", &p[i][j]); 13 14 } 15 16 void init() { 17 for(int i = 0; i < (1<<n); ++i) { 18 LL a = -1, b = -1, c = 0, d = 0; 19 for(int j = 0; j < n; ++j) 20 if((1<<j)&i) { 21 a = max(a, (LL)p[j][0]); 22 b = max(b, (LL)p[j][1]); 23 c += p[j][2]; 24 d += (LL)p[j][0]*p[j][2]*p[j][1]; 25 } 26 val[i] = a*b*c - d; 27 } 28 } 29 30 void solve() { 31 memset(dp, 0x3f, sizeof(dp)); 32 int a = (1<<n); 33 dp[0][0] = 0; 34 for(int i = 1; i <= k; ++i) 35 for(int j = 1; j < a; ++j) { 36 dp[i][j] = val[j]; 37 for(int tk = j; tk; tk = (tk-1)&j) 38 dp[i][j] = min(dp[i][j], dp[i-1][tk]+val[j^tk]); 39 } 40 41 printf("%lld\n", dp[k][a-1]); 42 } 43 44 int main() { 45 input(); 46 init(); 47 solve(); 48 return 0; 49 }
D题:0/1分数规划 + 树形依赖背包dp,这题就是这两个算法和起来的一个裸题。代码如下:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <vector> 6 7 using namespace std; 8 const int maxn=2500+100; 9 const double eps=1e-7; 10 const double inf=1e20; 11 int n,k,sz; 12 double d[maxn]; 13 int s[maxn],p[maxn],fa[maxn]; 14 vector<int>G[maxn]; 15 double dp[maxn][maxn]; 16 int dfs_clock; 17 int order[maxn],pos[maxn],en[maxn]; 18 void dfs(int u){ 19 order[u]=++dfs_clock; 20 pos[dfs_clock]=u; 21 for(int i=0;i<G[u].size();i++){ 22 int v=G[u][i]; 23 dfs(v); 24 } 25 en[u]=dfs_clock; 26 } 27 28 bool check(double mid){ 29 for(int i=1;i<=n;i++) 30 d[i]=p[i]-mid*s[i]; 31 for(int i=1;i<=n+1;i++){ 32 for(int j=0;j<=k;j++){ 33 dp[i][j]=-inf; 34 } 35 } 36 dp[1][0]=0; 37 for(int i=1;i<=n;i++){ 38 for(int j=0;j<=k;j++){ 39 if(dp[i][j]<=-inf)continue; 40 if(j<k){ 41 dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+d[pos[i]]); 42 } 43 dp[en[pos[i]]+1][j]=max(dp[en[pos[i]]+1][j],dp[i][j]); 44 } 45 } 46 if(dp[n+1][k]>=0)return true; 47 return false; 48 } 49 50 int main(){ 51 scanf("%d%d",&k,&n); 52 for(int i=1;i<=n;i++){ 53 scanf("%d%d%d",&s[i],&p[i],&fa[i]); 54 G[fa[i]].push_back(i); 55 } 56 for(int i=1;i<=n;i++) 57 if(!order[i]) 58 dfs(i); 59 60 double l=0,r=10200; 61 while(r-l>eps){ 62 double mid=(l+r)/2; 63 if(check(mid)){ 64 l=mid; 65 }else{ 66 r=mid; 67 } 68 } 69 printf("%.3f\n",l); 70 return 0; 71 }
E题:FFT,我们推一下,首先看样例一:BABA距离为1,即BA的,有2个;距离为2,0个,距离为3,即BABA,有1个。怎么得到的呢?将第一个A与第一个B匹配,将第二个A与前两个B匹配,这个过程就像多项式乘法,自然想到FFT。再具体一点,我们把B所在的位置记为集合(0,2),把A所在的位置记为集合(1,3),那么我们发现,0和1匹配是一组长度为1的解,0和3匹配是一组长为3的解,而2不能和1匹配,因为我们要求B在A前面。2可以和3匹配是一组长度为1的解。这就是一个多项式乘法的过程,(x0+x2)*(x1+x3),但是我们最后的结果: x0+3x3+x5 ,但是这里有个问题,有一个x3是不合法的,而而我们没法分辨出来,我们可以这样写(x0+x-2)*(x1+x3),这样一来就OK了,我们看看结果是: x-1+2x3+x5我们立马就可以分辨出来 x-1 是不合法的,但是又有一个问题:FFT无法处理指数为负的多项式咋办?这个好办,我们假设这是一个n-1次的多项式,那么我们给B的多项式的每一项都乘以上xn就行了,这样的话,样例就变为(x4+x2)*(x1+x3),这样的话,答案就变为x3+2x5+x7,这样指数小于等于4的都是不合法的。这样,所有问题都解决了。代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const double pi = acos(-1.0); 4 5 struct comp{ 6 double r,i; 7 comp(double _r=0, double _i=0):r(_r),i(_i) {} 8 comp operator + (const comp& x) { return comp(r+x.r, i+x.i); } 9 comp operator - (const comp& x) { return comp(r-x.r, i-x.i); } 10 comp operator * (const comp& x) { return comp(r*x.r-i*x.i, r*x.i+i*x.r); } 11 }; 12 13 void FFT(comp a[], int n, int t) { 14 for(int i=1, j=0; i < n-1; i++) { 15 for(int s=n; j^=s>>=1, ~j&s;); 16 if(i<j) swap(a[i],a[j]); 17 } 18 for(int d=0; (1<<d)<n; d++) { 19 int m=1<<d, m2=m<<1; 20 double o=pi/m*t; 21 comp _w(cos(o),sin(o)); 22 for(int i=0; i<n; i+=m2) { 23 comp w(1,0); 24 for(int j=0; j<m; j++) { 25 comp& A=a[i+j+m], &B=a[i+j], t=w*A; 26 A=B-t; B=B+t; w=w*_w; 27 } 28 } 29 } 30 if(t == -1) for(int i=0; i<n; i++) a[i].r=floor(a[i].r/n+0.5); 31 } 32 33 const int maxn = 3e6 + 100; 34 comp A[maxn], B[maxn]; 35 char str[maxn]; 36 int len; 37 38 int main() { 39 scanf("%s", str); 40 len = strlen(str); 41 for(int i = 0; i < len; ++i) { 42 if(str[i] == 'B') A[i].r=0, B[len-i].r=1; 43 else A[i].r=1, B[len-i].r=0; 44 } 45 int tmp = 1; 46 while(tmp < 2*len) tmp*=2; 47 swap(len, tmp); 48 FFT(A, len, 1); 49 FFT(B, len, 1); 50 for(int i = 0; i < len; ++i) 51 A[i] = A[i]*B[i]; 52 FFT(A, len, -1); 53 for(int i = tmp+1; i < 2*tmp; ++i) { 54 printf("%.0f\n", A[i].r); 55 } 56 return 0; 57 }
F题:dp,设 f[i][j] 表示前 i 列用了长度 j 的木条,则转移就是 f[i][j] = f[i][j] + f[i-1][j-k],最后把 f[w][0] +...+ f[w][n] 加起来再减去不符合情况的就行了。不符合情况的只有 min(h,n/w)+1 种。代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int f[105][10005]; 5 const int mod = 1e9+7; 6 7 int main() { 8 int n, w, h; 9 scanf("%d%d%d",&n,&w,&h); 10 f[0][0] = 1; 11 for(int i = 1; i <= w; ++i) 12 for(int j = 0; j <= n; ++j) 13 for(int k = 0; k <= min(h,j); ++k) 14 f[i][j] = (f[i][j]+f[i-1][j-k])%mod; 15 16 int ans = 0; 17 for(int i = 0; i <= n; ++i) 18 ans = (ans+f[w][i])%mod; 19 ans = (ans-min(h,n/w)-1+mod)%mod; 20 printf("%d\n", ans); 21 return 0; 22 }
G题:空
H题:空
I 题:lca ,直接上板子就好了。代码如下:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <queue> 6 #include <cmath> 7 using namespace std; 8 const int maxn=2e5+100; 9 int head[maxn],Next[2*maxn],to[2*maxn]; 10 int n,sz,dep; 11 void add_edge(int a,int b){ 12 ++sz; 13 to[sz]=b;Next[sz]=head[a];head[a]=sz; 14 } 15 int f[maxn][40],dist[maxn],d[maxn]; 16 queue<int>q; 17 void bfs(){ 18 q.push(1);d[1]=1; 19 while(!q.empty()){ 20 int u=q.front();q.pop(); 21 for(int i=head[u];i!=-1;i=Next[i]){ 22 int v=to[i]; 23 if(d[v])continue; 24 d[v]=d[u]+1; 25 dist[v]=dist[u]+1; 26 f[v][0]=u; 27 for(int j=1;j<=dep;j++){ 28 f[v][j]=f[f[v][j-1]][j-1]; 29 } 30 q.push(v); 31 } 32 } 33 } 34 35 int lca(int x,int y){ 36 if(d[x]>d[y])swap(x,y); 37 for(int i=dep;i>=0;i--){ 38 if(d[f[y][i]]>=d[x])y=f[y][i]; 39 } 40 if(x==y)return x; 41 for(int i=dep;i>=0;i--){ 42 if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; 43 } 44 return f[x][0]; 45 } 46 47 int main(){ 48 sz=0; 49 memset(head,-1,sizeof(head)); 50 scanf("%d",&n); 51 int a,b; 52 dep=(int)(log(n)/log(2))+1; 53 for(int i=1;i<n;i++){ 54 scanf("%d%d",&a,&b); 55 add_edge(a,b); 56 add_edge(b,a); 57 } 58 bfs(); 59 for(int i=1;i<=n;i++) 60 dist[i]++; 61 long long ans=0; 62 for(int i=2;i<=n;i++) 63 ans+=dist[i]; 64 for(int i=2;i<=n/2;i++){ 65 for(int j=i+i;j<=n;j+=i){ 66 ans+=dist[i]+dist[j]-2*dist[lca(i,j)]+1; 67 } 68 } 69 printf("%lld\n",ans); 70 return 0; 71 }
J题:空
K题:空