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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

J题:空

K题:空

 

转载于:https://www.cnblogs.com/DynastySun/p/9740893.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值