zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ",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
    ", 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
    ", 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
    ",ans);
    70 return 0;
    71 }
    View Code

    J题:空

    K题:空

  • 相关阅读:
    C#中将全部代码一次性折叠
    C#中图片单击旋转事件
    块参照重命名
    补强圈设计
    c# winform 按名称取得控件
    获得某控件的父控件(容器)中的所有控件
    回车键当Tab键使用
    替换CAD中原有命令为开发人员自己开发的命令的方法
    窗体设置
    判断控件的tag是否为空的方法
  • 原文地址:https://www.cnblogs.com/DynastySun/p/9740893.html
Copyright © 2011-2022 走看看