The Preliminary Contest for ICPC Asia Nanjing 2019ICPC南京网络赛
?
B.super_log (歐拉降冪)
?題意
定一個一個運算log*,迭代表達式為
給定一個a,b計算直到迭代結果>=b時,最小的x,輸出對m取余后的值?
?思路
$log*_{a}(a^{a})=1+log*_{a}(a)$? ? ? ? ? ?①
$log*_{a}(a)=1+log*_{a}(log_{a}(a))$? ? ②
$log*_{a}(log_{a}(a))=log*_{a}(1)=-1$? ③
①②帶入③得 $log*_{a}(a^{a})=2$
....
以此類推得$log*_{a}(a^{a^{a^{a^{a^{...}}}}})=b$? ? (共b個a)?
接下來接轉化為$a^{a^{a^{a^{a^{...}}}}}$??(共b個a)? 對m取模得結果
ps.如果對歐拉降冪不熟悉的話可以先看一下這個題(戳我~)
利用歐拉降冪
$a^=\begin{cases}a^{b\%\varphi(p)}? \ \ \ \ \ \ \ \ \ \? gcd(a,p)=1 \\ a^ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \? ?gcd(a,p)\neq 1,b \leqslant \varphi(p)\\a^{b\%\varphi(p)+\varphi(p)}? \ \ gcd(a,p)\neq1,b\geqslant \varphi(p)? \\ \end{cases}$
遞歸求解,至多走到2log層模數就會變成1,返回答案就行了。
但是在求$a^{a}\%\varphi(p)$時怎么計算$a$和$\varphi(p)$的大小呢
利用取模方法!
在取模時,如果大于$\varphi(p)$就取模 x=x%mod,否則不取模x=x,就可以當作a和p的互素處理
具體證明請看這里
??代碼
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 ll qpow(ll a,ll b,ll mod)//快速冪 5 { 6 ll res=1; 7 while(b) 8 { 9 if(b&1) 10 res=res*a>mod?res*a%mod+mod:res*a; 11 a=a*a>mod?a*a%mod+mod:a*a; 12 b>>=1; 13 } 14 return res; 15 } 16 17 ll phi(ll x)//求x的歐拉函數值 18 { 19 ll res=x; 20 for(int i=2;i*i<=x;i++) 21 { 22 if(x%i==0) 23 { 24 while(x%i==0) 25 x/=i; 26 res=res-res/i; 27 } 28 } 29 if(x>1) 30 res=res-res/x; 31 return res; 32 } 33 34 ll solve(ll a,ll b,ll m) 35 { 36 if(m == 1) 37 return 0; 38 if(b==0||a==1) 39 return 1ll; 40 41 ll p=phi(m); 42 return qpow(a,solve(a,b-1,p),m); 43 } 44 45 int main() 46 { 47 int t; 48 cin>>t; 49 while(t--) 50 { 51 ll a,b,m; 52 cin>>a>>b>>m; 53 cout<<solve(a,b,m)%m<<endl; 54 } 55 } View Code?
H. Holy Grail(最短路floyd)
??題意
給一個加權有向圖,然后讓你給你六個頂點,添六條邊,
但是添邊是有限制的。每次添邊的權值要最小,并且不能構成負環,
?
??思路
可以先根據已知邊跑floyd,獲得此時兩點u->v的最短路,然后v->u的權值等于u->v的最短路取反
然后再根據此時已知邊繼續跑floyd,再加邊
進行6次floyd,加6條邊
?dij不能處理負權pass!
?
(在比賽時一直在想加上u->v這條邊也不能構成環的情況然后就不知道該怎么做了...
然而看出題人題解貌似沒有這個意思....??題意描述不清喵喵喵?)
??代碼
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define inf 0x3f3f3f3f3f3f3f3f 5 int n,m; 6 ll d[305][305]; 7 8 void Init() 9 { 10 for(int i=1;i<=n;i++) 11 for(int j=1;j<=n;j++) 12 d[i][j]=inf; 13 } 14 15 void floyd() 16 { 17 for(int k=1;k<=n;k++) 18 for(int i=1;i<=n;i++) 19 for(int j=1;j<=n;j++) 20 d[i][j]=min(d[i][j],d[i][k]+d[k][j]); 21 } 22 int main() 23 { 24 // freopen("C:\\Users\\14685\\Desktop\\C++workspace\\in&out\\contest","r",stdin); 25 int t; 26 scanf("%d",&t); 27 while(t--) 28 { 29 scanf("%d%d",&n,&m); 30 Init(); 31 int u,v; 32 ll k; 33 for(int i=1;i<=m;++i) 34 { 35 scanf("%d%d%lld",&u,&v,&k); 36 d[u+1][v+1]=k; 37 } 38 for(int i=1;i<=6;++i) 39 { 40 floyd(); 41 scanf("%d%d",&u,&v); 42 printf("%lld\n",-1ll*d[v+1][u+1]); 43 d[u+1][v+1]=-1ll*d[v+1][u+1]; 44 } 45 } 46 } View Code?
F.Greedy Sequence(思維+滑動窗口)
?題意
給你一個包含 n 個數的序列 a,其中 a 中存的數為 1~n 的排列;
定義一個二維數組 s;
s 中一共有 n 行,每行有 n 個數;
定義 s 數組:
① $s_{i}[1]=i$
②$\forall\ ?i \in[1,n],j \in [2,n],s_{i,j} \le s_{i,j-1}$,即 $s_i$是個非增序列;
③對于 $s_i$ 中的第 j( j > 1 ) 個數,$s_{i,j}$ 在 a 中的位置與 $s_{i,j-1}$ 在 a 中的位置之差的絕對值不能超過 k;
?、?$s_i$ 中的第 j( j > 1) 個數,要盡可能的大;
?、萑绻?$s_{i,j}$ 后,在 a 中找不到滿足條件 ②③ 的數,并且 $s_i$ 不足 n 個數,用 0 填充剩余的數;
輸出 |s1|,|s2|,...,|sn|,|si|表示 s 中第 i 行包含的數的個數;
?思路
在set為空時插入0,是為了upper_bound()容易找,
利用upper_bound()需要-1,如果upper_bound()=begin()的話需要判斷
插入0后就不需要判斷了,方便操作
?代碼
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+5; 4 int a[maxn]; 5 int R[maxn],L[maxn]; 6 int s[maxn]; 7 set<int> f; 8 int n,k; 9 10 void Solve() 11 { 12 f.clear(); 13 f.insert(0); 14 int index=2;///index代表下標 15 for(;index<=min(k+1,n);index++) 16 f.insert(a[index]); 17 18 for(int i=1;i<=n;i++) 19 { 20 auto it=f.upper_bound(a[i]); 21 it--; 22 R[a[i]]=*it; 23 f.erase(f.find(a[i+1])); 24 if(index<=n) 25 f.insert(a[index++]); 26 } 27 f.clear(); 28 f.insert(0); 29 index=n-1; 30 for(;index>=max(1,n-k);index--) 31 f.insert(a[index]); 32 33 for(int i=n;i>=1;i--) 34 { 35 auto it=f.upper_bound(a[i]); 36 it--; 37 L[a[i]]=*it; 38 f.erase(f.find(a[i-1])); 39 if(index>=1) 40 f.insert(a[index--]); 41 } 42 // for(int i=1;i<=n;i++) 43 // printf("%d R:%d L:%d\n",a[i],R[a[i]],L[a[i]]); 44 45 s[0]==0; 46 for(int i=1;i<=n;i++) 47 { 48 s[i]=s[max(R[i],L[i])]+1; 49 printf("%d%c",s[i],i==n?'\n':' '); 50 } 51 52 } 53 int main() 54 { 55 int t; 56 cin>>t; 57 while(t--) 58 { 59 cin>>n>>k; 60 for(int i=1;i<=n;i++) 61 cin>>a[i]; 62 Solve(); 63 } 64 } View Code?
I.Washing clothes(貪心)
?題意
有n個人分別在$t_{i}$時刻進入洗衣房要洗衣服,可以機洗可以手洗,
但是只有一臺洗衣機,每次機洗花費的時間是$x$,同時也可以手洗花費時間是$m$,
可以多人同時手洗,而不能同時機洗
求當$x\epsilon?[1,y]$,所有人所要花費的最少時間。
?思路
?
?如何判斷從哪個人開始使用洗衣機呢?
我們可以使手洗時間盡可能的與機洗時間相同,找到這個點,即為最小值
為什么這個點是最小值呢?
因為$f(i)$是單調遞增函數,$g(i)$是單調遞減函數
設兩者相等時機洗$cnt$個,手洗$n-cnt$個
①現在讓手洗多一個,機洗少一個,
因為$f(i)$單調遞增,$f(n-cnt+1)>f(n-cnt)$,所以$max(f(i),g(i))$增大,花費時間增加
②現在讓手洗少一個,機洗多一個,
手洗花費時間減少$f(i)<g(i)$,已經不影響最大值,而機洗時間可能增大,花費時間可能增加
?
如何來計算這一點
可以利用手洗和機洗的時間關系,計算手洗一件可以機洗cnt件,
那后$cnt$件可以機洗,前$n-cnt$件用手洗(手洗早結束也早,所以是在cnt件機洗結束時結束)
?
這里解釋一下題解中機洗答案為什么是$max_{j=i}^{N}t_{j}+(n-j+1)*x$
用洗衣機洗有兩種情況:
①洗衣機無空閑時間。排隊等著前一個人用完洗衣機后一個人接著用
其中$t_{j}+(n-j+1)*x$其實是相當于把后面的衣服都在$j$洗完以后接著洗的,
也就是人來了在等洗衣機洗完,洗完接著又開始洗下一個人的衣服,計算無空閑時間情況
②洗衣機有空閑時間。前一個人用完洗衣機,后一個人還沒到,隔一段時間才到然后用洗衣機
從i到n取最大值,其實是假設第$j$個人洗完,而第$j+1$個人還沒來
那第$j$個人肯定不會影響最大值了,第$j$個人的時間肯定在$t[j+1]$之內了,計算有空閑時間情況
?
?代碼
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int maxn=1e6+5; 5 ll a[maxn]; 6 7 int main() 8 { 9 int n,m; 10 while(~scanf("%d%d",&n,&m)) 11 { 12 for(int i=1;i<=n;i++) 13 scanf("%lld",a+i); 14 15 sort(a+1,a+1+n); 16 17 for(int i=1;i<=m;i++) 18 { 19 int cnt=min(n,m/i);///手洗一個能機洗的cnt個 20 ///令手洗與機洗結束時間相同 21 ///后cnt個機洗,前n-cnt個手洗 22 ll ans=0; 23 if(cnt<n) 24 ans=a[n-cnt]+m;///手洗所用時間 25 for(int j=n-cnt+1;j<=n;j++)///機洗 26 ans=max(ans,a[j]+1ll*(n-j+1)*i); 27 28 printf("%lld%c",ans,i==m?'\n':' '); 29 } 30 } 31 } View Code?
轉載于:https://www.cnblogs.com/MMMinoz/p/11447578.html
總結
以上是生活随笔為你收集整理的The Preliminary Contest for ICPC Asia Nanjing 2019ICPC南京网络赛的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kubectl 安装
- 下一篇: 原创:古埃及文明真伪:为何中国考古越发现