zoukankan      html  css  js  c++  java
  • [考试反思]1008csp-s模拟测试65:突袭

    博客园挂了,不让粘图。

    写的朴素一点。

    #1:100+100+25=225

    #2:100+70+35=205

    #2:100+60+45=205(我)

    回到第一机房还算不错的第一仗。

    考完之后我以为我AK了然而T2被卡常打成暴力,T3贪心伪证了(虽说是全场最高分)

    全程在思考。很好啊。

    继续保持。

    注意常数,在卡常题上要花些时间优化打法卡常。

    T1:Simple

    做法比较傻逼。

    互质下才好做,所以把nm都干掉gcd,把这样贡献的答案先算上。

    我们考虑列出一个表,每n个一行(n<=m)。

    这样如果某一个数是m的倍数,那么从这里开始这一列就不会出现坏数了。

    那么只要每一列算最早什么时候会出现m的倍数就好了,ex_gcd。

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 #define int long long
     5 int gcd(int a,int b){return b?gcd(b,a%b):a;}
     6 void ex_gcd(int a,int b,int &x,int &y){
     7     if(!b){x=1;y=0;return;}
     8     ex_gcd(b,a%b,x,y);
     9     int r=x;x=y;y=r-a/b*y;
    10 }
    11 main(){//freopen("ex_simple2.in","r",stdin);
    12     int t;scanf("%lld",&t);
    13     while(t--){
    14         int n,m,q,ans=0,x,y,g;
    15         scanf("%lld%lld%lld",&n,&m,&q);
    16         if(n>m)n^=m^=n^=m;
    17         g=gcd(n,m);ans+=q-q/g;
    18         n/=g;m/=g;q/=g;
    19         ex_gcd(n,m,x,y);x%=m;//printf("x=%lld
    ",x);
    20         for(int i=1;i<n;++i)ans+=min(((m-i)*x%m+m)%m,(q-i+n)/n);//,printf("%lld
    ",ans);
    21         printf("%lld
    ",ans);
    22     }
    23 }
    View Code

    T2:Walk

    卡常题。

    1000000的w以内最多有240个约数,所以做dp找每个约数的最长链就好了。

     1 //1000000以内每个数最多有240个约数,而平均只有14个
     2 //注意卡内存:61079552个int
     3 #include<cstdio>
     4 #include<iostream>
     5 #include<unordered_map>
     6 using namespace std;
     7 int FIR[1000005],L[14000000],TO[14000000],CNT;
     8 void add(int a,int d){L[++CNT]=FIR[a];FIR[a]=CNT;TO[CNT]=d;}
     9 int fir[400005],l[800005],to[800005],w[800005],cnt;
    10 void link(int a,int b,int v){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;w[cnt]=v;}
    11 int ans[400005],n;
    12 unordered_map<int,int>dp[400005];
    13 int read(){
    14     register int p=0;register char ch=getchar();
    15     while(ch<'0'||ch>'9')ch=getchar();
    16     while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar();
    17     return p;
    18 }
    19 void dfs(int p,int fa){
    20     for(int i=fir[p];i;i=l[i])if(to[i]!=fa){
    21         dfs(to[i],p);
    22         for(int j=FIR[w[i]];j;j=L[j]){
    23             int x=dp[to[i]][TO[j]],y=dp[p][TO[j]];
    24             ans[x+y+1]=max(ans[x+y+1],TO[j]);
    25             dp[p][TO[j]]=max(y,x+1);
    26         }
    27         dp[to[i]].clear();
    28     }
    29 }
    30 int main(){//freopen("t2.in","r",stdin);freopen("my.out","w",stdout);
    31     n=read();int mx=0;
    32     for(int x,y,w,i=1;i<n;++i)x=read(),y=read(),w=read(),link(x,y,w),link(y,x,w),mx=max(mx,w);
    33     for(int i=1;i<=mx;++i)for(int j=i;j<=mx;j+=i)add(j,i);
    34     dfs(1,0);
    35     for(int i=n;i;--i)ans[i]=max(ans[i],ans[i+1]);
    36     for(int i=1;i<=n;++i)printf("%d
    ",ans[i]);
    37 }
    View Code

    然后就T成暴力了。

    改为对于每个约数建边,根号硬筛而不是预处理,就A了。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<vector>
     4 using namespace std;
     5 vector<int>a[1000004],b[1000005];
     6 int fir[400005],l[800005],to[800005],cnt;
     7 void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;}
     8 int ans[400005],lgst;
     9 int read(){
    10     register int p=0;register char ch=getchar();
    11     while(ch<'0'||ch>'9')ch=getchar();
    12     while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar();
    13     return p;
    14 }
    15 int dfs(int p,int fa){
    16     int mx=0;
    17     for(int i=fir[p];i;i=l[i])if(to[i]!=fa){
    18         int x=dfs(to[i],p)+1;
    19         lgst=max(lgst,mx+x);mx=max(mx,x);
    20     }fir[p]=0;return mx;
    21 }
    22 int main(){//freopen("ex_walk2.in","r",stdin);freopen("my.out","w",stdout);
    23     register int n=read();
    24     for(int t=1,x,y,w;t<n;++t){
    25         x=read(),y=read(),w=read();
    26         for(int i=1;i*i<=w;++i)if(w%i==0){
    27             a[i].push_back(x),b[i].push_back(y);
    28             if(i*i!=w)a[w/i].push_back(x),b[w/i].push_back(y);
    29         }
    30     }
    31     for(int i=1;i<=1000000;++i){
    32         for(int j=0;j<a[i].size();++j)link(a[i][j],b[i][j]),link(b[i][j],a[i][j]);
    33         for(int j=0;j<a[i].size();++j)if(fir[a[i][j]])dfs(a[i][j],0);
    34         ans[lgst]=max(ans[lgst],i);
    35         cnt=lgst=0;
    36     }
    37     for(int i=n;i;--i)ans[i]=max(ans[i],ans[i+1]);
    38     for(int i=1;i<=n;++i)printf("%d
    ",ans[i]);
    39 }
    View Code

    T3:Travel

    思路懂了,还想到了优化。

    但是下午就要考试显然没时间改了。

    upd:所以新一场的T3还是不可改。

    但是这道题至少也是想了大半个上午了好歹记一下思路。

    口胡预警

    我们考虑,如果L<=s-1那么向左走到头再向右是最优决策的备选方案。

    另一种决策就是先向右走到头,再向左走到头,再向右走一段直到用尽向右的步数。

    具体怎么算先不说,先考虑另一半情况L>s-1。

    那么n-L-1<n-s。可以发现n-L-1就是向右的步数,n-s就是右边还有几个位置。

    在把序列翻转之后和上面是完全等价的。这两个不等式至少满足其一。

    所以如果满足L<=s-1就把序列翻转。那么决策就只有右左和左右左这两种决策了。

    这就减少了很多的分类讨论量,代码应该会简洁一些。

    右左的答案好说,就是2x[n]-x[1]-x[s]。方案也不难构造,两三个for循环即可。

    而左右左的话,我们要从s+1开始枚举终点e。这样的话s左边和e右边都可以消耗等同于长度的向左步数。代价是长度的2倍。

    而s和e之间的一段我们要耗尽剩下的向左步数。从中选出最短的几段来消耗左步数,代价为3倍,其它的代价为1倍。

    终点每右移1位,你可以选的线段就多了1个,需要选的线段就多了2个。

    故不存在反悔。开一个堆维护即可。

    其实没必要在这时候就尝试构造方案,你找到最优的终点之后构造其实就简单了。

    但是细节还是比较多,代码还是比较麻烦。

    所以代码先咕了。

     upd1012:咕了4天总算A掉了!开心

     1 #include<cstdio>
     2 #include<queue>
     3 using namespace std;
     4 int n,L,s,to[200005],alt[200005],rev;long long x[200005],ans,y[200005];
     5 int main(){//freopen("travel6.in","r",stdin);freopen("my.out","w",stdout);
     6     scanf("%d%d%d",&n,&L,&s);
     7     for(int i=1;i<=n;++i)scanf("%lld",&x[i]);
     8     if(L==0&&s!=1){puts("-1");return 0;}
     9     if(L==n-1&&s!=n){puts("-1");return 0;}
    10     if(L<s-1){
    11         int mx=x[n];
    12         for(int i=1;i<=n;++i)y[i]=mx-x[n-i+1];
    13         for(int i=1;i<=n;++i)x[i]=y[i];
    14         L=n-1-L;s=n+1-s;rev=1;
    15     }//全部转化为右左或左右左
    16     priority_queue<int,vector<int>,greater<int> >q;
    17     long long alc=0,ans1=2*x[n]-x[1]-x[s],ans2=2*(x[n]-x[1]),E,ans=2*(x[n]-x[1]);
    18     for(int e=s+1;e<=n;++e){
    19         ans-=x[e]-x[e-1];
    20         while(alc<L-(s-1+n-e))ans+=q.top()*2,alc++,q.pop();
    21         q.push(x[e+1]-x[e]);
    22         if(ans<ans2)ans2=ans,E=e;
    23     }
    24     if(ans1<ans2){int R=n-1-L;printf("%lld
    ",ans1);
    25         for(int i=s+1;i<=s+R-1;++i)printf("%d ",rev?n-i+1:i);
    26         for(int i=n;i>=s+R;--i)printf("%d ",rev?n-i+1:i);
    27         for(int i=s-1;i;--i)printf("%d ",rev?n-i+1:i);
    28     }else{printf("%lld
    ",ans2);
    29         priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
    30         for(int i=s+2;i<=E;++i)q.push(make_pair(x[i]-x[i-1],i));
    31         if(E!=n)L--;alt[s]=1;
    32         int pos=s;while(L>1&&pos>1)to[pos]=pos-1,alt[pos-1]=1,pos--,L--;
    33         if(pos!=1)to[pos]=1,alt[1]=1;
    34         pos=n;if(E!=n)L++;while(L&&pos>E)to[pos]=pos-1,alt[pos-1]=1,pos--,L--;
    35         while(L)to[q.top().second]=q.top().second-1,alt[q.top().second-1]=1,L--,q.pop();
    36         for(int i=1;i<=n;++i)if(!to[i])for(int j=i+1;;++j)if(!alt[j]){to[i]=j;alt[j]=1;break;}
    37         //for(int i=n;i;--i)printf("%d
    ",rev?n-to[i]+1:to[i]);return 0;
    38         for(int i=to[s];;i=to[i]){printf("%d ",rev?n-i+1:i);if(i==E)return 0;}
    39     }
    40 }
    View Code
  • 相关阅读:
    laravel使用redis报错
    PHP新特性W3Cschool
    【python】跳过验证直接登陆-cookie已经知道需要部分直接注入
    【python】显示等待
    【python】pymysql库的简单使用
    【python】UI自动化优化浏览器重复打开和跑脚本时电脑无法做其他工作的问题
    【python】seleniumwire和selenium的区别
    【python】UI自动化-新版
    【python】UI自动化获取输入框的值
    【python】UI自动化多窗口处理
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11640865.html
Copyright © 2011-2022 走看看