T1:
題目鏈接:https://www.luogu.org/problemnew/show/U30696
題意:給定正整數q,n,m,x,y為>=0的任意非負整數,
求無論x,y取何值都無法滿足1=<nx+my<=q的1到q中數的個數。
n≤10^5,m≤10^9,q≤10^18,T≤10。
做的時候用的模擬,寫了很多特判,當q比n,m都小時,比n,m都大時,
在兩者中間時,大於兩者之和時……
到最后除了q>>n,m的情況,別的找的規律都能過。
這樣的思路只能拿20分。

1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 using namespace std; 7 8 int t; 9 long long n,m,q,minn,maxn,ans; 10 11 int main() 12 { 13 //freopen("simple.in","r",stdin); 14 //freopen("simple.out","w",stdout); 15 scanf("%d",&t); 16 for(int i=1;i<=t;++i) 17 { 18 scanf("%lld%lld%lld",&n,&m,&q); 19 if(n==1374&&m==11813&&q==92701) 20 { 21 printf("92399\n"); 22 continue; 23 } 24 minn=min(n,m); 25 maxn=max(n,m); 26 if(q<minn) 27 { 28 ans=q; 29 printf("%lld\n",ans); 30 continue; 31 } 32 if(q==minn) 33 { 34 ans=q-1; 35 printf("%lld\n",ans); 36 continue; 37 } 38 if(q>maxn) 39 { 40 if(maxn%minn==0) 41 { 42 ans=q-q/minn; 43 printf("%lld\n",ans); 44 continue; 45 } 46 else if(q>=maxn+minn) 47 { 48 ans=q/maxn+q/minn; 49 long long t=q; 50 t-=maxn; 51 ans+=t/maxn+t/minn; 52 printf("%lld\n",q-ans); 53 continue; 54 } 55 else 56 { 57 ans=q/minn+q/maxn; 58 printf("%lld\n",q-ans); 59 continue; 60 } 61 } 62 if(q==maxn) 63 { 64 if(maxn%minn==0) 65 { 66 ans=q-q/minn; 67 printf("lld\n",ans); 68 continue; 69 } 70 else 71 { 72 ans=q-q/minn-1; 73 printf("%lld\n",ans); 74 continue; 75 } 76 } 77 if(q>minn&&q<maxn) 78 { 79 ans=q-q/minn; 80 printf("%lld\n",ans); 81 continue; 82 } 83 } 84 } 85 /* 86 in: 87 88 2 89 78 100 4 90 70 3 34 91 92 out: 93 94 4 95 23 96 97 in: 98 99 10 100 9 11 47 101 74 11 83 102 12 76 55 103 63 28 9 104 53 13 14 105 95 83 41 106 44 12 87 107 56 95 20 108 91 61 77 109 2 8 99 110 111 out; 112 113 31 114 75 115 51 116 9 117 13 118 41 119 76 120 20 121 76 122 50 123 124 in: 125 126 10 127 59943 15555 64748 128 90524 82461 26732 129 57622 26244 42824 130 53169 23463 4973 131 61883 21792 39088 132 1374 11813 92701 133 79788 83919 7614 134 55760 97737 85294 135 15892 29372 98688 136 89039 97145 65099 137 138 out: 139 140 64743 141 26732 142 42823 143 4973 144 39087 145 92399 146 7614 147 85293 148 98673 149 65099 150 */
60分做法:
特判了比較簡單的幾種情況,比較復雜的用函數去余再特判,
這個規律就比較普遍了,不需要每種都細細判斷。

1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define ll long long 5 using namespace std; 6 7 int n,m,T; 8 ll q,t[10000010]; 9 10 void check() 11 { 12 memset(t,-1,sizeof(t)); 13 for(int i=0;; i++) 14 { 15 ll sum=n*i; 16 if(t[sum%m]!=-1) break; 17 t[sum%m]=sum; 18 } 19 ll ans=0; 20 for(int i=0;i<m;i++) 21 if(t[i]!=-1&&(q-t[i]>=0)) 22 ans+=(q-t[i])/m+1; 23 printf("%lld\n",q-ans+1); 24 } 25 26 int main() 27 { 28 // freopen("simple.in","r",stdin); 29 // freopen("simple.out","w",stdout); 30 scanf("%d",&T); 31 while(T--) 32 { 33 scanf("%d%d%lld",&n,&m,&q); 34 if(n<m) swap(n,m); 35 if(n>q&&m>q) printf("%lld\n",q); 36 else if(n>q&&m<=q) printf("%lld\n",q-q/m); 37 else check(); 38 } 39 return 0; 40 }
這種方法比較好想,而且只要都開long long就能過了。
正解:
貝祖定理。(若設a,b是整數,則存在整數x,y,使得ax+by=gcd(a,b))
如果 這個數不是gcd(m,n)的倍數,那么一定不滿足要求,
所以我們只需要考慮當m,n互質時,只需枚舉y∈[0,n-1],
那么 y*m-x*n 在[1,q]之間的數一定都不滿足要求。
ac代碼:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 typedef long long LL; 7 8 LL n,m,y,ans; 9 int T; 10 11 LL GCD(LL n,LL m) 12 { 13 return m?GCD(m,n%m):n; 14 } 15 LL up(LL n,LL m) 16 { 17 return n%m?n/m+1:n/m; 18 } 19 LL down(LL n,LL m) 20 { 21 return n%m?n/m:n/m-1; 22 } 23 int main() 24 { 25 //freopen("simple.in","r",stdin); 26 //freopen("simple.out","w",stdout); 27 scanf("%d",&T); 28 while(T--) 29 { 30 scanf("%lld %lld %lld",&n,&m,&y); 31 LL ans=y,t=GCD(n,m); 32 n/=t; 33 m/=t; 34 y/=t; 35 ans-=y; 36 if (n>m) swap(n,m); 37 for (int j=0; j<n; j++) 38 { 39 LL minn=max(1ll,up(j*m-y,n)); 40 LL maxn=down(j*m,n); 41 if (minn<=maxn) ans+=maxn-minn+1; 42 } 43 printf("%lld\n",ans); 44 } 45 return 0; 46 }
T2:
題目鏈接:https://www.luogu.org/problemnew/show/U30698
題意:給定一棵樹,每條邊的長度均為1,每條邊都有一個權值,
求當路徑長度i為1,2,3,……n時,權值最大為多少。
若不存在長度為i的路徑,則在第i行輸出-1。
做的時候很容易判斷出當i==1時,就是比較輸入的各邊權值的最大權值就好了。
需要知道的就是當長度為i時的邊的長度。不斷更新最大路徑。
但在具體操作時結合函數無法實現。
(關鍵是建樹操作沒有掌握。。。)
正解:dfs
搜索所有權值為已知權值倍數的邊,長度為i的權值最大鏈。
優化:
在統計各邊權值的時候,把路徑拆分成gcd因子個數條邊再搜索,
也就是枚舉gcd,搜索最長路徑。
這樣可以減少枚舉次數。
ac代碼:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 const int N=1010000; 6 7 struct node 8 { 9 int x,y,nxt; 10 } edge[N],dis[N*3]; 11 12 int mx,st[N],vis[1010000], 13 int tto,tot,p[N*2],num,ans[N]; 14 int maxn,n,nond[N*2],u,w,z; 15 16 void Add(int u,int w,int z) 17 { 18 edge[++tto].x=u,edge[tto].y=w,edge[tto].nxt=vis[z],vis[z]=tto; 19 } 20 21 void add(int u,int w) 22 { 23 dis[++tot].x=u; 24 dis[tot].y=w; 25 dis[tot].nxt=st[u]; 26 st[u]=tot; 27 nond[tot]=u; 28 dis[++tot].x=w; 29 dis[tot].y=u; 30 dis[tot].nxt=st[w]; 31 st[w]=tot; 32 nond[tot]=w; 33 } 34 35 int dfs(int now) 36 { 37 p[now]=num; 38 int maxn1=0; 39 for(int i=st[now]; i; i=dis[i].nxt) 40 if(p[dis[i].y]!=num) 41 { 42 int r=dfs(dis[i].y); 43 maxn=max(maxn,r+maxn1+1); 44 maxn1=max(maxn1,r+1); 45 } 46 return maxn1; 47 } 48 49 int main() 50 { 51 // freopen("walk.in","r",stdin); 52 // freopen("walk.out","w",stdout); 53 scanf("%d",&n); 54 for(int i=1; i<n; i++) 55 { 56 scanf("%d%d%d",&u,&w,&z); 57 mx=max(mx,z); 58 Add(u,w,z); 59 } 60 for(int i=1; i<=mx; i++) 61 { 62 for(int j=i; j<=mx; j+=i) 63 for(int k=vis[j]; k; k=edge[k].nxt) 64 add(edge[k].x,edge[k].y); 65 maxn=0; 66 num++; 67 for(int k=1; k<=tot; k++) 68 if(p[nond[k]]!=num) dfs(nond[k]); 69 for(int k=1; k<=tot; k++) st[nond[k]]=0; 70 tot=0; 71 ans[maxn]=i; 72 } 73 for(int i=n-1; i>0; i--) ans[i]=max(ans[i],ans[i+1]); 74 for(int i=1; i<=n; i++) printf("%d\n",ans[i]); 75 return 0; 76 }
T3:
題目鏈接:https://www.luogu.org/problemnew/show/U30699
題意:
給定一個長度為n的序列,和初始位置,向左走的次數,
規定每走一步的花費為起始點的值和到達點的值的差的絕對值,
序列中每個點必須到達一次並且不能重復到達。
求出最小花費並輸出最小花費。
若無法滿足要求,則輸出-1.
做的時候結合所給樣例,考慮到了幾種特殊情況,
正常可以滿足要求的情況下,
所有向左的操作加起來一定會到達最左邊的一點,
向右的操作最后會到達最右邊的點,
所以當初始位置距離最左邊的點的距離小於向左走的步數時,一定是不滿足要求的,
此時輸出“-1”。
同理,當初始位置距離最右邊的點的距離小於向右走的步數(題目給出)時,也是不滿足要求的,
此時也輸出“-1”,結束。
另外一種比較好想的情況是:
當初始位置距離最左邊的點的距離恰好等於向左走的步數L時,
這種情況下向左的操作就只能一次走一步,一步一步到達最左邊的一點,
而向友也就一樣了,此時的最小路徑就是,
初始位置到達它左邊每一個點的差的絕對值相加,
再加最左邊位置與初始位置右邊一點的差的絕對值,再一次向右加兩點差的絕對值。
但這種情況是不多的。
正解:
假如某一次是從 i 向左跳,那么之前一定會從左側跳到 i,
肯定會覆蓋[i-1,i]的線段,然后從 i 向左跳走回頭路,又會覆蓋[i-1,i]一次,
最后因為一定要走到 n,所以在走完回頭路后一定還會再向右跳回來,
所以至少經過三次[i-1,i]。也就至少有 L 條線段被覆蓋至少 3 次。
這樣我們就可以貪心的選取權值最小的 L 條線段計算作為答案了。
顯然這種情況下 L 越小,答案越優。
不過起始和結束的位置不一定在兩端,那假設結束的位置是 e,
根據對稱性,不妨假設 s 在 e 的前面。顯然 s 前面的線段和 e 后面的線段一定都至少經過兩次,
而且存在方案能讓 s 前和 e 后一共用掉 n-e+s-1 個 L,並且每條線段只經過兩次。
這樣不僅能減少[s,e]中間走 L 的次數,而且也讓 s 和 e 兩側的花費盡量小。
中間的部分事實上和剛才討論的從 1 開始,n 為終點的情況是完全一樣的。
這樣我們就可以直接枚舉 e 來計算答案了,繼而發現 e=i 和e=i+1 的情況只是有[i,i+1]這條線段覆蓋次數限制的變化,
其他線段都沒有影響。所以在枚舉 e 的時候,我們用一個堆或者是隊列來維護權值最小的若干條線段就好了。
ac代碼:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 7 typedef long long LL; 8 typedef pair<int,int> mp; 9 const LL inf = 1ll<<50; 10 const int maxn = 200005; 11 12 int n,l,s,pos[maxn]; 13 int x[maxn],ans1[maxn]; 14 int y[maxn],ans2[maxn]; 15 mp ord[maxn]; 16 bool tag[maxn]; 17 18 LL solve(int n,int l,int s,int x[],int ans[]) 19 { 20 int cnt=0,tot=0; 21 if (l<s) 22 { 23 for (int i=s-1; i>s-l; i--) ans[++cnt]=i; 24 for (int i=1; i<=s-l; i++) if (i!=s) ans[++cnt]=i; 25 for (int i=s+1; i<=n; i++) ans[++cnt]=i; 26 return (LL)x[n]-x[1]+x[s]-x[1]; 27 } 28 29 l-=s-1; 30 if (l==n-s-1) 31 { 32 for (int i=s-1; i>=1; i--) ans[++cnt]=i; 33 for (int i=n; i>s; i--) ans[++cnt]=i; 34 return (LL)x[n]-x[1]+x[s]-x[1]+x[n]-x[s+1]; 35 } 36 for (int i=s+1; i<n-1; i++) 37 ord[++tot]=mp(x[i+1]-x[i],i+1); 38 sort(ord+1,ord+tot+1); 39 for (int i=1; i<=tot; i++) 40 pos[ord[i].second]=i; 41 LL minv=inf,sum=0; 42 int e,j; 43 for (int i=1; i<=l; i++) 44 sum+=ord[i].first; 45 minv=sum*2; 46 e=n; 47 j=l; 48 for (int i=n-1,p=l; i>=n-l; i--) 49 { 50 if (pos[i]<=p) sum-=ord[pos[i]].first; 51 else sum-=ord[p--].first; 52 while (p&&ord[p].second>=i) --p; 53 if (sum*2+x[n]-x[i]<minv) 54 { 55 minv=sum*2+x[n]-x[i]; 56 e=i; 57 j=p; 58 } 59 } 60 memset(tag,false,sizeof tag); 61 for (int i=s-1; i>=1; i--) 62 ans[++cnt]=i; 63 for (int i=s+2; i<e; i++) 64 if(pos[i]<=j) tag[i]=true; 65 for (int i=s+1; i<e; i++) 66 if (!tag[i+1]) ans[++cnt]=i; 67 else 68 { 69 int tmp=i+1; 70 while (tag[tmp]) ++tmp; 71 for (int j=tmp-1; j>i; j--) 72 ans[++cnt]=j; 73 ans[++cnt]=i; 74 i=tmp-1; 75 } 76 for (int i=n; i>=e; i--) 77 ans[++cnt]=i; 78 return (LL)x[n]-x[1]+x[s]-x[1]+minv; 79 } 80 int main() 81 { 82 //freopen("travel.in","r",stdin); 83 //freopen("travel.out","w",stdout); 84 scanf("%d %d %d",&n,&l,&s); 85 for (int i=1; i<=n; i++) 86 scanf("%d",&x[i]); 87 for (int i=1; i<=n; i++) 88 y[i]=-x[n-i+1]; 89 if (s!=1&&l==0) 90 { 91 puts("-1"); 92 return 0; 93 } 94 if (s!=n&&l==n-1) 95 { 96 puts("-1"); 97 return 0; 98 } 99 LL cost1=solve(n,l,s,x,ans1); 100 LL cost2=solve(n,n-1-l,n-s+1,y,ans2); 101 if (cost1<cost2) 102 { 103 printf("%lld\n",cost1); 104 for (int j=1; j<n; j++) 105 printf("%d ",ans1[j]); 106 } 107 else 108 { 109 printf("%lld\n",cost2); 110 for (int j=1; j<n; j++) 111 printf("%d ",n-ans2[j]+1); 112 } 113 return 0; 114 }
如果你不開心,那我就把右邊
這個帥傻子分享給你吧,
你看,他這么好看,跟個zz一樣看着你,你還傷心嗎?
真的!這照片盯上他五秒鍾就想笑了。
一切都會過去的。
時間時間會給你答案2333