【jzoj】2018.2.5NOIP普及组——C组模拟赛
前言
今天第一次正式C組題,不過……比較恐怖。
正題
題目1:公牛和母牛(jzoj1292)
有n頭牛,可以是公牛或母牛,每頭公牛之間至少得有k頭母牛。求方案數。
輸入輸出(建議跳過)
Input
第一行:兩個空格隔開的整數N(N<=100000)和K。
Output
輸出一個整數表示方法總數,答案可能很大,所以只需輸出mod 5,000,011的值即可。
Sample Input
4 2
Sample Output
6
解題思路
推出動態轉移方程,用f[i]來表示i頭牛的方案數。
f[i]=f[i?k]+f[i?1](i>k+1)f[i]=f[i?k]+f[i?1](i>k+1)
因為如果在i處放置了公牛那么前k個就不能放,所以是f[i-k],放母牛的話就累加之前的方案數f[i-1]
代碼
#include<cstdio> using namespace std; int n,k,f[100001]; int main() {scanf("%d%d",&n,&k);k++;//加f[0]=1;//初始化for (int i=1;i<=n;i++){if (i>k){f[i]=(f[i-k]+f[i-1])%5000011;//動態轉移}else{f[i]=(1+f[i-1])%5000011;//動態轉移}}printf("%d",f[n]);//輸出 }題目2:最短路(jzοj3777)
每個點的權值遵守以下規律
f[1][1]=1
f[i][j]=f[i-1][j]+f[i][j-1]
然后求(1,1)到(m,n)的最小權值 mod 10000000007。
輸入輸出(建議跳過)
Input
一行兩個正整數N,M,表示圖的大小。
Output
一行一個整數Ans,表示答案模1000000007后的值。
Sample Input
1 2
Sample Output
6
解題思路
楊輝三角,也就是組合,然后用快速冪求組合公式
然后取膜的快速冪
cnm(modp)=((m!(n?m)!)?m!(p?2)(modp)+n)(modp)cmn(modp)=((m!(n?m)!)?m!(p?2)(modp)+n)(modp)
代碼
#include<cstdio> #include<algorithm> #define mod 1000000007 using namespace std; long long n,m,s,x,y; long long qsm(long long x,long long c)//快速冪 {long long ans=1;while (c){if (c&1) {ans=(ans*x)%mod;}x=(x*x)%mod;c>>=1;}return ans; } int main() {scanf("%lld%lld",&n,&m);if (n<m) swap(n,m);x=1;y=1;for (long long i=n+2;i<=n+m+1;i++) x=(x*i)%mod;//求m!(n-m)!for (long long i=1;i<=m;i++) y=(y*i)%mod;//m!printf("%lld",(x*qsm(y,mod-2)%mod+n)%mod);//公式 }題目3:Magical GCD(jzoj3780)
給出一個長n的序列,i到j的價值為i到j的最大公約數乘以它的長度。求最大價值
輸入輸出(建議無視)
Input
單個測試點包含多組數據。
輸入的第一行是一個整數T表示數據組數。
每組數據的第一行是一個整數N,描述序列長度。
接下來N個數字,描述這個序列元素A[i]。
Output
對于每組測試數據輸出一行,包含一個整數,表示序列最大的 Magical GCD。
Sample Input
1
5
30 60 20 20 20
Sample Output
80
解題思路
暴力枚舉左到右會超時,所以我們用g[i]表示從i到r的gcd。
然后我們會發現這樣會有許多重復的,只要我們過濾掉重復的gcd就好了。
所以我們先用一個右指針和左指針,發現重復時,就可以利用指針在下次枚舉時無視自己
代碼
#include<cstdio> #include<iostream> #define ll long long using namespace std; int ls[100001],nx[100001],t,n; ll a[100001],g[100001],maxs; ll gcd(ll x,ll y)//輾轉相除法 {if (x%y==0) return y;else return gcd(y,x%y); } int main() {scanf("%d",&t);for (int ti=1;ti<=t;ti++){maxs=0;scanf("%d",&n);for (int i=1;i<=n;i++){scanf("%lld",&a[i]);g[i]=a[i];nx[i]=i+1;//左指針ls[i]=i-1;//右指針}for (int r=1;r<=n;r++)for (int i=1;i<=r;i=nx[i]){g[i]=gcd(g[i],a[r]);//求最大公約maxs=max(g[i]*(r-i+1),maxs);//求最大值if (g[i]==g[i-1])//發現重復{nx[ls[i]]=nx[i];//修改指針讓下次無視自己ls[nx[i]]=ls[i];//同上}}printf("%lld\n",maxs);} }題目4:Multiset(jzoj3781)
有一個數0,有以下兩種操作
讓一個數增加x=x+1
將一個數分裂為兩個非負整數
給出一個序列,求出至少要多少次操作才可以變為這個序列
輸入輸出(建議無視)
Input
第一行為一個整數N,描述最終集合的大小。
第二行為N個非負整數,為最終集合的每一個元素。
Output
輸出唯一一行,Alice 最少玩的輪數。
Sample Input
Sample Input 1
1
0
Sample Input 2
4
1 1 1 1
Sample Input 3
5
0 3 0 3 0
Sample Output
Sample Output 1
0
Sample Output 2
3
Sample Output 3
5
解題思路
首先感謝紀中dalao的幫助
反推,就變成了減去1和合并這兩種操作。
然后貪心:用桶w[i]來表示在第i步有多少個數會變為0
代碼
#include<cstdio> #include<iostream> using namespace std; int n,x,maxs,s; int w[1000001]; int main() {scanf("%d",&n);for (int i=1;i<=n;i++){scanf("%d",&x);w[x]++;//桶maxs=max(maxs,x);//記錄最大值}s=w[0];//0的個數for (int i=1;i<=maxs;i++){s=(s+1)/2+w[i];//合并0并加入新的0}while (s>1) {s=(s+1)/2;maxs++;}//合并剩余部分printf("%d",maxs);//輸出 }總結
以上是生活随笔為你收集整理的【jzoj】2018.2.5NOIP普及组——C组模拟赛的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 旧电脑如何提炼出黄金电脑主机里面的黄金如
- 下一篇: 2600才是现在性价比最高的电脑平台26