码迷,mamicode.com
首页 > 其他好文 > 详细

【做题】Ocd、Mancity、Captcha

时间:2017-09-30 21:52:40      阅读:253      评论:0      收藏:0      [点我收藏+]

标签:break   条件   val   set   复杂   use   答案   getch   using   

Ocd

令人震惊的一点在于,像我一样无脑puts("-1")的,居然有40分。

然而经大佬指点,在每一位上求填某个数对答案的贡献是可以直接计算出来的。

具体实现是通过dp预处理。定义dp[i][j]为1~i的排列中逆序对数为j的方案数。

对于状态转移,枚举新的一位填上了第几大的数。

于是我们有 dp[i][j]=∑dp[i-1][k] (j-i+1<=k<=j)。对此可以用前缀和进行优化。

因此预处理的时间复杂度为O(n*k)。

然后,我们一位位地计算答案。设当前为第i位,目前选取的数是j,需要有k组逆序对,j在可选数的字典序中排第val[j]个,那么选取j的贡献就是dp[n-i][k-val[j]+1]。

因此,我们可以当前位按字典序从小到大进行枚举,如果其贡献值大于等于p则必然选取这个数,否则把p减去其贡献值。

具体实现时用used数组记录每个数是否可选,并通过差分优化val数组的计算。

时间复杂度O(n*k+n^2)。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 const int N=1010,K=10010,INF=1e12;
 5 int dp[N][K],g[N][N],val[N],ans[N],num[N];
 6 bool used[N];
 7 int n,k,p;
 8 int get_dp(int x,int y)
 9 {
10     if(y==0) return dp[x][y];
11     return dp[x][y]-dp[x][y-1];
12 }
13 signed main()
14 {
15     scanf("%lld%lld%lld",&n,&k,&p);
16     for(int i=1;i<=n;i++)
17      for(int j=1;j<=n;j++) scanf("%lld",&g[i][j]);
18     for(int i=0;i<=k;i++) dp[1][i]=1,dp[0][i]=i+1;
19     for(int i=2;i<=n;i++)
20     {
21         for(int j=0;j<=k;j++)
22         {
23             if(j-i<0) dp[i][j]+=dp[i-1][j];
24             else dp[i][j]+=dp[i-1][j]-dp[i-1][j-i];
25             dp[i][j]=min(dp[i][j],INF);
26             dp[i][j]+=dp[i][j-1];
27         }
28     }
29     if(get_dp(n,k)<p) return 0*puts("-1");
30     int le=k,now=p;
31     for(int i=1;i<=n;i++)
32     {
33         memset(val,0,sizeof(val));
34         for(int j=1;j<=n;j++) if(used[j]) val[j+1]--;
35         for(int j=1;j<=n;j++) val[j]+=val[j-1];
36         for(int j=1;j<=n;j++) val[j]+=j-1;
37         for(int j=1;j<=n;j++) num[j]=val[g[i][j]];
38         for(int j=1;j<=n;j++) if(!used[g[i][j]]&&num[j]<=le)
39         {
40             if(get_dp(n-i,le-num[j])>=now)
41             {
42                 le-=num[j];
43                 ans[i]=g[i][j];
44                 used[g[i][j]]=1;
45                 break;
46             }
47             else now-=get_dp(n-i,le-num[j]);
48         }
49     }
50     for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
51     return 0*printf("\n");
52 }

 

Mancity

非常令人在意的是每条边边权只为1或2的性质。我由此得出了每个小时(除了最后一个)必然走d或d-1的长度。于是我由此确定答案的上下界,random了一下,骗了60分。

然而那还需要卡常的正解并没有用到这个性质。

注意到路径是可以拓展的,也就是说,记top[i][j]为i结点像根节点最远能到达的点,那么top[i][j]=top[top[i][k]][j-k] (0<=k<=j)。

以及如下这个结论:对于路径的两个端点a,b,以及它们的LCA z,我们可以让a和b尽可能地向z移动,最后如果a,b重合则得到答案,如果a,b的距离小于等于d则让ans+1,否则让ans+2,然后这就是最优解。

对于这个结论的正确性,假设有更优解,那么记它跨越z的那个小时是从c到d(c是初始a的祖先),那么要么最后a(b)一定与c(d)重合或在其上面,就是其中一个符合这个条件,另一个可以通过不多于1次的移动来满足这一条件。而先前的每一次移动都是贪心最优的,因此这一定是最优解。

于是我们可以充分地利用倍增的思想,对上述算法进行优化。

具体就是记录top[i][j]表示从i向上移动2^j步所最远能到达的点。(求top[i][0]可以用倍增或二分,求LCA我又用了一个倍增)。

时间复杂度O(n*logn)。(然而我还要用快读优化)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=500010,MAXPOS=19;
 4 int n,d,q;
 5 int fa[N][MAXPOS+1],dis[N][MAXPOS+1],top[N][MAXPOS+1],deep[N];
 6 void prework()
 7 {
 8     int le,pos;
 9     for(int i=1;i<=n;i++)
10     {
11         for(int j=1;j<=MAXPOS;j++)
12         {
13             fa[i][j]=fa[fa[i][j-1]][j-1];
14             dis[i][j]=dis[i][j-1]+dis[fa[i][j-1]][j-1];
15         }
16         le=d;pos=i;
17         for(int j=MAXPOS;j>=0;j--)
18         {
19             if(dis[pos][j]<=le) le-=dis[pos][j],pos=fa[pos][j];
20         }
21         top[i][0]=pos;
22         for(int j=1;j<=MAXPOS;j++)
23         {
24             top[i][j]=top[top[i][j-1]][j-1];
25         }
26     }
27 }
28 int lca(int a,int b)
29 {
30     if(deep[a]<deep[b]) swap(a,b);
31     for(int i=MAXPOS;i>=0;i--)
32     {
33         if(deep[fa[a][i]]>=deep[b]) a=fa[a][i];
34     }
35     if(a==b) return a;
36     for(int i=MAXPOS;i>=0;i--)
37     {
38         if(fa[a][i]!=fa[b][i])
39          a=fa[a][i],b=fa[b][i];
40     }
41     return fa[a][0];
42 }
43 int main()
44 {
45     int x,y,ans,z;
46     scanf("%d%d%d",&n,&d,&q);
47     deep[1]=1;
48     for(int i=2;i<=n;i++)
49     {
50         scanf("%d%d",&x,&y);
51         fa[i][0]=x;
52         dis[i][0]=y;
53         deep[i]=deep[x]+1;
54     }
55     prework();
56     for(int i=1;i<=q;i++)
57     {
58         ans=0;
59         scanf("%d%d",&x,&y);
60         z=lca(x,y);
61         for(int j=MAXPOS;j>=0;j--)
62         {
63             if(deep[top[x][j]]>deep[z]) x=top[x][j],ans+=(1<<j);
64             if(deep[top[y][j]]>deep[z]) y=top[y][j],ans+=(1<<j);
65         }
66         if(x!=y)
67         {
68             if(dis[x][MAXPOS]+dis[y][MAXPOS]-2*dis[z][MAXPOS]<=d) ans++;
69             else ans+=2;
70         }
71         printf("%d\n",ans);
72     }
73     return 0;
74 }

 

Captcha

又是喜闻乐见的水题了,然而带有码农属性。

(用gets读入导致我爆30,而另一个用gets的同学爆0了。)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=100010;
 4 char pic[8][N*7];
 5 int num,sum,n,ans[N];
 6 int main()
 7 {
 8     scanf("%d",&n);
 9     getchar();
10     for(int i=1;i<=6;i++)
11     {
12         for(int j=1;j<=n;j++)
13          scanf("%s",pic[i]+7*j-6);
14     }
15     int pos=0,x,y;
16     for(int i=1;i<=n;i++,pos+=7)
17     {
18         num=0;
19         for(int j=1;j<=6;j++)
20         {
21             for(int k=1;k<=6;k++)
22             {
23                 if(pic[j][k+pos]==.)
24                 {
25                     if(num==0) y=k+pos;
26                     num++;
27                 }
28             }
29             if(num>0)
30             {
31                 x=j;
32                 break;
33             }
34         }
35         if(pic[x][y+1]==.&&pic[x][y+2]==.)
36         {
37             if(pic[x+1][y]==.&&pic[x+1][y+2]==.)
38             {
39                 if(pic[x+2][y+1]==#) ans[i]=0;
40                 else if(pic[x+3][y]==#) ans[i]=9;
41                 else ans[i]=8;
42                 continue;
43             }
44             if(pic[x+4][y]==#)
45             {
46                 ans[i]=7;
47                 continue;
48             }
49             if(pic[x+3][y]==.)
50             {
51                 if(pic[x+3][y+1]==#&&pic[x+3][y+2]==.) ans[i]=6;
52                 else ans[i]=2;
53                 continue;
54             }
55             if(pic[x+1][y]==#) ans[i]=3;
56             else ans[i]=5;
57         }
58         else
59         {
60             if(pic[x+1][y-1]==.) ans[i]=4;
61             else ans[i]=1;
62             continue;
63         }
64     }
65     for(int i=1;i<=n;i++)
66      printf("%d ",ans[i]);
67     return 0;
68 }

 

小结:靠rp骗了70分,然后又莫名被坑了70分。

【做题】Ocd、Mancity、Captcha

标签:break   条件   val   set   复杂   use   答案   getch   using   

原文地址:http://www.cnblogs.com/cly-none/p/7616142.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!