[考試反思]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\n",x);
20         for(int i=1;i<n;++i)ans+=min(((m-i)*x%m+m)%m,(q-i+n)/n);//,printf("%lld\n",ans);
21         printf("%lld\n",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\n",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\n",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\n",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\n",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\n",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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM