A 簽到
題解:直接輸出即可,注意行末換行。

#include <iostream> #include <fstream> using namespace std; int main(){ int n,m; freopen("in.txt","r",stdin); //輸入重定向,輸入數據將從in.txt文件中讀取 freopen("out.txt","w",stdout); //輸出重定向,輸出數據將保存out.txt文件中 cout<<"祝賀祖國成立71周年!"<<endl; return 0; }
B 不要666升級版
題解:
本題可以用數位DP來解:
首先定義狀態dp[len][sum1][sum2] 表示長度為len對6取模為sum1,各位上的數字和為sum2。
然后把一個數拆成a + b的形式,如878327 為 800000 + 78327。
由(a + b)^3 = a ^3 + b ^ 3 + 3 * a^2 * b + 3 * a * b ^ 2。
那么所有滿足條件的數的立方和,其中cnt為這些數字的個數:
a ^ 3 * cnt + (b ^ 3 + c ^ 3 + …) + 3 * a ^ 2 * (b + c + …) + 3 * a * (b ^ 2 + c ^ 2 +….)
我們發現:(b^3 + c ^ 3 + ….)為立方和,(b + c + …. )為和,(b ^2 + c ^ 2 + …)為平方和,所以我們需要維護4個值:這些數字的個數,和,平方和,立方和就可以解決這題了。

#include<bits/stdc++.h> using namespace std; typedef long long ll; int b[25]; ll bit[25]; const ll mod = 1e9 + 7; struct st{ ll num; ll sum; ll sqsum; ll cusum; st(ll _num,ll _sum,ll _sqsum,ll _cusum):num(_num),sum(_sum),sqsum(_sqsum),cusum(_cusum){ } st():num(-1),sum(0),sqsum(0),cusum(0){ } }dp[25][10][180]; st dfs(int pos, int num1, int num2, bool state){ if(pos ==- 1){ if(num1 == 0 || num2 % 6 == 0){ return st(0,0,0,0); } return st(1, 0, 0, 0); } if(!state && dp[pos][num1][num2].num != -1){ return dp[pos][num1][num2]; } int up = state ? b[pos] : 9; st ans; ans.num = 0; for(int i = 0;i <= up; i++){ if(i == 6){ continue; } st tem = dfs(pos - 1, (num1 * 10 + i) % 6, num2 + i, state && i == up); ans.num = (ans.num + tem.num) % mod; ans.sum = ( ans.sum + i * bit[pos] % mod * tem.num % mod + tem.sum) % mod; ans.sqsum = (ans.sqsum + tem.sqsum) % mod; ll temp = i * bit[pos] % mod; ans.sqsum = (ans.sqsum + temp * temp % mod * tem.num % mod) % mod; ans.sqsum = (ans.sqsum + 1ll * 2 * temp % mod * tem.sum % mod) % mod; ans.cusum = (ans.cusum + tem.cusum) % mod; ans.cusum = (ans.cusum + temp * temp % mod * temp % mod * tem.num % mod) % mod; ans.cusum = (ans.cusum + 1ll * 3 * temp % mod * temp % mod * tem.sum % mod) % mod; ans.cusum = (ans.cusum + 1ll * 3 * temp * tem.sqsum % mod) % mod; } if(!state){ dp[pos][num1][num2] = ans; } return ans; } ll solve(ll m){ int len = 0; while(m){ b[len++] = m % 10; m /= 10; } return dfs(len-1, 0, 0, true).cusum; } int main(){ bit[0] = 1; for(int i = 1; i <= 20; i++){ bit[i] = bit[i-1] * 10%mod; } ll n, m; // srand((unsigned int)time(NULL)); // freopen("test3.out","w",stdout); // freopen("test3.in","r",stdin); // int t = 5000; // while(t--){ // n = ((double)rand() / RAND_MAX) * 1000000000000000000; // m = ((double)rand() / RAND_MAX) * 1000000000000000000; // if(n > m){ // n = n ^ m; // m = n ^ m; // n = n ^ m; // } // if(n < 0 || m < 0) continue; // printf("%lld %lld\n", n, m); // } // while(scanf("%lld%lld",&n, &m)!=EOF){ printf("%lld\n",(solve(m) - solve(n - 1) + mod) % mod); } return 0; }
C 歐濤的生日聚會
題解:
如果整個圖沒有環的話,顯然最多能分的種類數是每個連通分量內最長鏈的長度之和。
如果整個圖是由若干個不相交的環構成的話,最多能分的種類數是所有環長度的最大公約數(找環的時候,可以從連通塊內的任意一點開始編號,第二次經過一個點的時候,它第二次的編號減去第一次的編號就是環的大小)。
除了這兩種特殊情況之外,還有一種情況是兩個環之間可能有公共部分。
我們 DFS 的時候可以倒推:建圖的時候不僅連一條u→v,邊權為 1 的邊之外,同時連一條 v→u,邊權為 −1 的邊(這種連邊方式可以確保我們從任意一個點開始,都可以遍歷整個連通塊)。
對於每個連通塊,我們還是任意選一個點開始編號,經過一條邊的時候將編號加上邊權,和上面一樣,第二次經過同一個點的時候,它第二次的編號減去第一次的編號就是環的大小。
這樣我們就可以找出所有的環了。
(最后別忘了題目要求面具最少要有三種)

#include <cstdio> #include <cstring> #include <algorithm> #include <set> #define INF 1e9 using namespace std; struct edge { int v,w; bool operator<(const edge&a)const { return v<a.v||(v==a.v&&w<a.w); } }; set<edge> e[100005]; int dis[100005],vis[100005],ans,res,cnt,maxv,minv; int gcd(int x,int y) { return y==0?x:gcd(y,x%y); } void dfs(int u,int d) { if(dis[u]) { ans=gcd(ans,abs(d-dis[u])); return; } dis[u]=d,vis[u]=1; maxv=max(maxv,dis[u]); minv=min(minv,dis[u]); for(auto i:e[u]) dfs(i.v,d+i.w); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); e[u].insert({v,1}); e[v].insert({u,-1}); } for(int i=1;i<=n;i++) if(!vis[i]) { maxv=-INF,minv=INF; dfs(i,1); res+=maxv-minv+1; } if(ans) { if(ans<3)puts("-1 -1"); else for(int i=3;i<=ans;i++) if(ans%i==0) { printf("%d %d\n",ans,i); break; } } else { if(res<3)puts("-1 -1"); else printf("%d 3\n",res); } return 0; }
D 歐濤玩游戲
題解:
直接判斷輸入的時候可以構成直角三角形即可,注意多組輸入、不要用int 否則是答案錯誤

#include <iostream> #include <fstream> using namespace std; int main(){ int a,b,c; // ifstream infile; //輸入流 // ofstream outfile; //輸出流 // // infile.open("C:\\Users\\z1164\\Desktop\\校賽數據\\玩游戲.in", ios::in); while (cin>>a>>b>>c) // 若未到文件結束一直循環 { // infile >> a >> b >> c; //cout<<a<<" "<<b<<" "<<c<<endl; if ((a*a==b*b+c*c)||(b*b==a*a+c*c)||(c*c==a*a+b*b)) cout<<"YES"<<endl; else cout<<"NO"<<endl; } //cin>>a>>b>>c; return 0; }
E 生而為人、我很抱歉
題解:
題目描述可能有問題、只需要考慮輸入的字符串彼此之間是否符合字典序,不用考慮單個字符串是否是按字典序排序。

#include<bits/stdc++.h> using namespace std; int main(){ int n; int m; while(cin>>n>>m){ string s1[101]; string s2[105]; int flag=1; for(int i=1;i<=n;i++){ cin>>s1[i]; s2[i]=s1[i]; } sort(s2+1,s2+1+n); for(int i=1;i<=n;i++){ if(s2[i]!=s1[i]){ flag=0; break; } } if(flag)puts("YES"); else puts("NO"); } }
F 糖果店
題解:
可以看出 Bob可以購買的是 這個數組包含最大值和最小值的子串個個數,因此我們從左到右遍歷這個序列,用兩個指針t1,t2 來表示到目前為止,最大值和最小值位置的最大值,則當前位置對答案的貢獻就是
min( t1 + t2 )。
Jerry就容易很多,統計出最大值和最小值的數量,則最終的答案就是從最大值構成的集合中選取非0個個數的方式*從最小值構成的集合中選取非0個個數的方式*從除去最大值和最小值構成的集合中選取任意個數的方式。
ps:c(n,0) + c(n,1) + c(n,2) + .....+ c(n,n) = 2n.
題意中忘了寫模數,表示非常抱歉

#include<iostream> #include<cstring> #include<algorithm> using namespace std; #define ll long long const ll inf=0xffffff; const ll mod=1000000007; ll n,m,k,t; ll num[100010]; ll cal(ll a,ll b) { ll ans=1; while(b) { if(b&1) ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans; } int main() { cin>>t; while(t--) { cin>>n; ll min1=inf,max1=0,min_num=0,max_num=0; ll sum1=0,sum2=0; for(int i=1;i<=n;i++) { cin>>num[i]; min1=min(min1,num[i]); max1=max(max1,num[i]); } if(min1==max1) { sum1=n*(n+1)/2%mod; sum2=cal(2,n)-1; cout<<sum1<<' '<<sum2<<endl; continue; } ll t1=0,t2=0; for(int i=1;i<=n;i++) { if(min1==num[i]) { t1=i; min_num++; } if(max1==num[i]) { t2=i; max_num++; } sum1=(min(t1,t2)+sum1)%mod; } sum2=((cal(2,min_num)-1)%mod*(cal(2,max_num)-1)%mod*cal(2,n-max_num-min_num))%mod;//排列組合 /*sum2=(cal(2,n)-cal(2,n-max_num)-cal(2,n-min_num)+cal(2,n-max_num-min_num))%mod;//容斥原理 if(sum2<0) sum2+=mod;*/ cout<<sum1<<' '<<sum2<<endl; } return 0; }
G 鑰匙
題解:
設lock[i]表示:有 i個槽的鑰匙的個數。
設one[i]表示:有 i個槽且左邊第一個槽深度為1 的鑰匙的個數。
設two[i]表示:有 i個槽且左邊第一個槽深度為2 的鑰匙的個數。
..
..
設six[i]表示:有 i個槽且左邊第一個槽深度為6的鑰匙的個數。
則顯然:lock[i]=one[i]+two[i]+ three[i]+four[i]+five[i]+six[i]
由於易知:one[i]=six[i],two[i]=three[i]=four[i]=five[i]
則可以得到:lock[i]= one[i]*2+two[i]*4
其中:
one[i]=one[i-1]+two[i-1]+three[i-1]+four[i-1]+five[i-1]+a[i];
=one[i-1]+two[i-1]*4 +a[i]
two[i]=one[i-1]*2+two[i-1]*4 + b[i]
one[i]=one[i-1]+two[i-1]+three[i-1]+four[i-1]+five[i-1]+a[i]=one[i-1]+two[i-1]*4 +a[i]
two[i]=one[i-1]*2+two[i-1]*4 +b[i]
其中,a[i] 和b[i]的含義分別是:
a[i]表示“有 i個槽且左邊第一個槽深度為1,同時這種鑰匙在除掉第一個槽后不再是鑰匙”的鑰匙的個數。
例如 123,124,125,134,135,145,126,136,146,156,1233
b[i]表示“有 i個槽且左邊第一個槽深度為2,同時這種鑰匙在除掉第一個槽后不再是鑰匙” 的鑰匙的個數。
關鍵的是求a[i]和b[i],我們可以得到如下表達式:
a[i]=(2^(i-1)-2)*6+(2^(i-2)-1)*4
b[i]=(2^(i-1)-2)*9
a[i] 的各部分的含義如下:
(2^(i-1)-2)*6:
當去掉第一位,后面i-1位只有 (2,3)或者 (2,4) 或者(2,5) 或者(3,4) 或者(3,5) 或者(4,5)兩種數字的時候,顯然是不合法的鑰匙(不滿足至少有3個不同的深度),加上第一位的1則顯然是一個合格的鑰匙。所以后面的數字可以為一個組合中兩個數字的任意一個,根據乘法原理i-1位一共有2^(i-1)種組合,當然還需要去掉兩種特殊情況,就是后面i-1位完全相同的情況。滿足這種條件的一共有上面六種組合,所以得到(2^(i-1)-2)*6。
(2^(i-2)-1)*4:
當去掉第一位,后面i-1位只有 (2,6)或者 (3,6) 或者(4,6) 或者(5,6)兩種數字的時候,顯然是不合法的鑰匙(不滿足至少有3個不同的深度),加上第一位的1則“可能”是一個合格的鑰匙。因為要注意滿足“相連的槽其深度之差不得為5”這個條件,所以,緊跟着1的絕對不能是6,這樣,相對於上面的分析,后面i-2位可以是每組中的任意一個,所以有2^(i-2),還要減去1是因為同樣要排除后面全部是和第2位一樣的數字這樣的情況。滿足這種條件的一共有上面的四種組合,所以得到(2^(i-2)-1)*4。
b[i] 的含義如下:
(2^(i-1)-2)*9:
當去掉第一位,后面i-1位只有 (1,3)或者 (1,4) 或者(1,5) 或者(3,4) 或者(3,5) 或者(3,6) 或者(4,5) 或者(4,6) 或者(5,6) 兩種數字的時候,顯然是不合法的鑰匙(不滿足至少有3個不同的深度),加上第一位的1則顯然是一個合格的鑰匙。所以后面的數字可以為一個組合中兩個數字的任意一個,根據乘法原理i-1位一共有2^(i-1)種組合,當然還需要去掉兩種特殊情況,就是后面i-1位完全相同的情況。滿足這種條件的一共有上面9種組合,所以得到(2^(i-1)-2)*9。
綜上我們就可以推出
lock[i]=one[i]*2+two[i]*4
one[i]=six[i]
two[i]=three[i]=four[i]=five[i]
one[i] = one[i-1]+two[i-1]*4 +a[i]
two[i] = one[i-1]*2+two[i-1]*4 +b[i]
a[i]=(2^(i-1)-2)*6+(2^(i-2)-1)*4
b[i]=(2^(i-1)-2)*9

#include<cstdio> #include<algorithm> #define ll long long using namespace std; ll s[3][30]; ll ans[30]; ll pow_1(int a){ int r=1; for(int i=1;i<=a;i++) r*=2; return r; } int main(){ int n; s[1][2]=0; s[2][2]=0; ans[1]=0; ans[2]=0; for(int i=3;i<=25;i++){ s[1][i]=s[1][i-1]+4*s[2][i-1]+6*(pow_1(i-1)-2)+4*(pow_1(i-2)-1); s[2][i]=2*s[1][i-1]+4*s[2][i-1]+9*(pow_1(i-1)-2); ans[i]=2*s[1][i]+4*s[2][i]; } while(scanf("%d",&n)!=EOF){ printf("%lld\n",ans[n]); } return 0; }