方程整數解
題意:
給定n,求出 a^2 + b^2 + c^2 = n(1<=n<=10000)的所有解,解要保證c>=b>=a>=1。
思路:
枚舉a和b,利用a和b推c,判斷是否合法即可
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
int n;
while(cin>>n){
int ok=0;
for(int a=1;a*a<n;a++){
for(int b=a;a*a+b*b<n;b++){
int cc=n-(a*a+b*b);
int c=sqrt(cc);
if(c>=b&&c*c==cc){
cout<<a<<' '<<b<<' '<<c<<endl;
ok=1;
}
}
}
if(!ok)cout<<"No Solution"<<endl;
}
return 0;
}
星系炸彈
題意:
有一個貝塔炸彈,a年b月c日放置,定時爲n天,請你計算它爆炸的準確日期。
n<=1e3
思路:
挺煩日期題的。
因爲n比較小,只有1000,所以可以直接一天一天加,很穩。
code:
#include<bits/stdc++.h>
using namespace std;
int s[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};//每個月的天數
bool check(int n){//判斷閏年
if(n%4==0&&n%100!=0)return 1;
if(n%400==0)return 1;
return 0;
}
signed main(){
int a,b,c,n;
while(cin>>a>>b>>c>>n){
while(n--){
if(check(a))s[2]=29;//如果是閏年則2月有29天
else s[2]=28;
c++;
if(c>s[b])c=1,b++;
if(b>12)b=1,a++;
}
printf("%04d-%02d-%02d\n",a,b,c);
}
return 0;
}
奇妙的數字
題意:
一個數的平方和立方正好把0~9的10個數字每個用且只用了一次。你能猜出這個數字是多少嗎?
思路:
因爲這個數一定存在,寫個程序直接while(1)從小到大不斷枚舉數x,然後把x2和x3拆位判斷是否每種數字都出現一次就行了。
最後計算出答案爲69。
牌型種數
題意:
從52張牌取13張,如果不考慮花色,只考慮點數,也不考慮自己得到的牌的先後順序
自己手裏能拿到的初始牌型組合一共有多少種呢?
思路:
因爲只需要輸出答案,直接枚舉每種點數的牌的數量,可以dfs也可以直接13個for循環。
答案爲3598180。
手鍊樣式
題意:
小明有3顆紅珊瑚,4顆白珊瑚,5顆黃瑪瑙。
他想用它們串成一圈作爲手鍊,送給女朋友。
現在小明想知道:如果考慮手鍊可以隨意轉動或翻轉,一共有多少不同的組合樣式?
思路:
因爲只需要輸出答案,所以可以不想那麼多,直接開map標記+全排列計算答案。
注意題目說明可以隨意轉動和翻轉,別漏了。
算出答案爲1170。
去別人那找個了組合數學的結論,順便掛下:
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
map<string,int>mark;
signed main(){
string s="aaabbbbccccc";//3+4+5=12
int ans=0;
do{
string t=s;
if(!mark[t])ans++;
for(int i=0;i<12;i++){//把翻轉和循環都標記掉
mark[t]=1;
reverse(t.begin(),t.end());//翻轉
mark[t]=1;
reverse(t.begin(),t.end());//翻回來
//循環一下,把第一個字符放到最後一個
char d=*t.begin();//取開頭
t.erase(t.begin());//刪掉開頭
t+=d;//放到結尾
}
}while(next_permutation(s.begin(),s.end()));
cout<<ans<<endl;
return 0;
}
飲料換購
題意:
開始有n瓶飲料,3個瓶蓋可以換一瓶,問最後一共喝到多少瓶。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
int n;
while(cin>>n){
int ans=n;
//n爲瓶蓋數
while(n>=3){
int t=n/3;//換到的飲料數
n-=t*3;//瓶蓋減少
ans+=t;//累加答案
n+=t;//瓶蓋增加
}
cout<<ans<<endl;
}
return 0;
}
壘骰子(矩陣快速冪)
題意:
賭聖atm晚年迷戀上了壘骰子,就是把骰子一個壘在另一個上邊,不能歪歪扭扭,要壘成方柱體。
經過長期觀察,atm 發現了穩定骰子的奧祕:有些數字的面貼着會互相排斥!
我們先來規範一下骰子:1 的對面是 4,2 的對面是 5,3 的對面是 6。
假設有 m 組互斥現象,每組中的那兩個數字的面緊貼在一起,骰子就不能穩定的壘起來。
atm想計算一下有多少種不同的可能的壘骰子方式。
兩種壘骰子方式相同,當且僅當這兩種方式中對應高度的骰子的對應數字的朝向都相同。
由於方案數可能過多,請輸出模 1e9+ 7 的結果。
思路:
非常容易就能想到dp的解法,但是硬莽的話只能過百分60的數據(n<=100)。
對於百分100的數據,n<=1e9,數據這麼大肯定是往快速冪方向想,應該是矩陣快速冪。
d(i,j)表示i個骰子,第j面朝上的做法,顯然d(i,j)=sigma d(i-1,k),其中j的對立面和k不互斥。
發現這個遞推關係只有最多6個加法,可以直接一次6階矩陣轉移計算出d(i),配合矩陣快速冪就很快了。
利用互斥關係構造轉移矩陣,矩陣快速冪即可。
注意每個固定住上下面的骰子可以可以旋轉,因此答案要乘上4n(4個面)。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=6;
const int mod=1e9+7;
struct Node{
int a[N][N];
};
int g[6][6];
int dif[6]={3,4,5,0,1,2};//每個數對面的數
Node mul(Node A,Node B){
Node C;
for(int i=0;i<N;i++)for(int j=0;j<N;j++)C.a[i][j]=0;
for(int k=0;k<N;k++)for(int i=0;i<N;i++)if(A.a[i][k]){
for(int j=0;j<N;j++)if(B.a[k][j]){
C.a[i][j]=(C.a[i][j]+A.a[i][k]*B.a[k][j])%mod;
}
}
return C;
}
Node Pow(Node a,int b){
Node ans;
for(int i=0;i<N;i++)for(int j=0;j<N;j++)ans.a[i][j]=(i==j);
while(b){
if(b&1)ans=mul(ans,a);
a=mul(a,a);
b>>=1;
}
return ans;
}
int ppow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
signed main(){
int n,m;
while(cin>>n>>m){
for(int i=0;i<6;i++)for(int j=0;j<6;j++)g[i][j]=1;
for(int i=1;i<=m;i++){//輸入m對互斥關係
int a,b;
cin>>a>>b;
a--,b--;
g[a][b]=g[b][a]=0;
}
Node x;
for(int i=0;i<6;i++)for(int j=0;j<6;j++)x.a[i][j]=0;
for(int i=0;i<6;i++)for(int j=0;j<6;j++){
if(g[i][j]){//dif[j]可從i轉移
x.a[i][dif[j]]=1;
}
}
Node p=Pow(x,n-1);
int ans[6]={0};//ans[i]即d[n][i]
for(int i=0;i<6;i++){//計算ans[],因爲初始矩陣爲1*6的全1矩陣,所以ans[i]就是x的第i列相加
for(int j=0;j<6;j++){
ans[i]+=p.a[i][j];
}
}
int sum=0;
for(int i=0;i<6;i++)sum=(sum+ans[i])%mod;
sum=sum*ppow(4,n)%mod;
cout<<sum<<endl;
}
return 0;
}
災後重建(壓軸題)
題意:
給定n個點m條帶權邊的無向圖
q組詢問,每組詢問問(L,R,K,C),要求輸出編號在[L,R]之間,且編號模K等於C的點,現在要你選出一些邊,使得這些點連通,問選出的邊權的最大值最小爲多少(保證每次詢問至少存在兩個點)。
(n<=5e4,m<=2e5,q<=5e5)
(L,R,K均在[1,n]以內,C在K以內)
時限5s
思路:
網上搜到的分級數據:
百分30數據nmq<=30
百分60數據nmq<=2000
百分100數據n<=5e4,m<=2e5,q<=1e6
最小生成樹能使得所有點聯通,因此先構建出最小生成樹,記錄樹邊,其他多餘的邊沒有用。
百分30:
做法1.能使得所有點聯通的就是最小生成樹,對於每組詢問 ,標記模k等於c的點,然後用已經記錄的樹邊跑kruskal,如果在連接某個邊之後,標記的點能夠聯通,那麼這個點的邊權就是答案。
做法2.二分最大邊權的最小值,然後在最小生成樹上從任意標記的點開始dfs,只走邊權小於等於當前二分的值的路徑,最後判斷能否把標記的點全部搜到。
百分60:
1.最小生成樹+LCA,在最小生成樹上記錄任意兩點之間路徑的最大值
2.kruskal重構樹+LCA,也是記錄兩點之間最大路徑最小值
對於每組詢問,對任意兩點之間的路徑最大值取max就是答案
百分100:
建議直接放棄
獎券數目
題意:
計算10000-99999中有多少個不帶4的數字,直接輸出答案
思路:
遍歷+拆位暴力判斷即可
三羊獻瑞
題意:
思路:
全排列枚舉每種漢字所代表的數,判斷是否合法即可
code:
#include<bits/stdc++.h>
using namespace std;
int a[10]={0,1,2,3,4,5,6,7,8,9};
//祥瑞生輝三羊獻氣
signed main(){
do{
if(a[0]&&a[4]){
int aa=a[0]*1000+a[1]*100+a[2]*10+a[3];
int bb=a[4]*1000+a[5]*100+a[6]*10+a[1];
int sum=a[4]*10000+a[5]*1000+a[2]*100+a[1]*10+a[7];
if(aa+bb==sum){
cout<<bb<<endl;
break;
}
}
}while(next_permutation(a,a+10));
return 0;
}
加法變乘法
題意:
我們都知道:1+2+3+ … + 49 = 1225
現在要求你把其中兩個不相鄰的加號變成乘號,使得結果爲2015
比如:
1+2+3+…+1011+12+…+2728+29+…+49 = 2015 就是符合要求的答案。
請你尋找另外一個可能的答案,並把位置靠前的那個乘號左邊的數字提交。
思路:
枚舉兩個乘號的位置,判斷是否合法即可。
計算出答案爲16,另寫一個程序直接輸出。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
int n=49;
for(int i=1;i<n;i++){//第一個乘號在i之後
for(int j=i+2;j<n;j++){//第二個乘號在j之後
int sum=0;
for(int k=1;k<=i-1;k++)sum+=k;
sum+=i*(i+1);
for(int k=i+2;k<=j-1;k++)sum+=k;
sum+=j*(j+1);
for(int k=j+2;k<=n;k++)sum+=k;
if(sum==2015)cout<<i<<endl;
}
}
return 0;
}
移動距離
題意:
思路:
這題的題面有點誤導人,看題可能會以爲只能沿着那個數字順序走,其實是可以直接上下左右走的。
因此這題其實就是計算兩個點的笛卡爾距離,算出座標差即可。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int w,m,n;
int getr(int x){//計算在第幾行
return x/w+(x%w!=0);
}
int getc(int x){//計算在第幾列
int t=getr(x);
x%=w;
if(t%2){//奇數正向
return x;
}else{//反向
return w-x+1;
}
}
signed main(){
while(cin>>w>>m>>n){//w爲一行的寬度
int ans=abs(getr(m)-getr(n));
ans+=abs(getc(m)-getc(n));
cout<<ans<<endl;
}
return 0;
}
生命之樹(樹形dp)
題意:
給定一顆n個節點的樹,每個點有點權
現在要求選出一個點集,滿足:
點集之內的任意兩點之間的路徑上的點也在這個點集中
問選出的點集的最大點權和是多少
思路:
首先要看懂題,題意就是選擇一些點,去掉其他點之後,選擇的這些點在同一個連通塊中,求最大點權和
令d(i)爲選擇點i所能得到的最大值,樹形dp:
考慮子節點v,如果d(v)大於0,就選擇v
考慮父節點,如果選擇父節點能使得答案變大,那麼d(x)就是答案,回溯到x的時候就能更新出更大的d(x)>d(v)
所以不需要管父節點,並不失完備性。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
vector<int>g[maxm];
int a[maxm];
int d[maxm];
void dfs(int x,int fa){
for(int v:g[x]){
if(v==fa)continue;
dfs(v,x);
if(d[v]>0)d[x]+=d[v];
}
d[x]+=a[x];
}
signed main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1,-1);
int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,d[i]);
}
cout<<ans<<endl;
return 0;
}
打印大X
題意:
小明希望用星號拼湊,打印出一個大X,他要求能夠控制筆畫的寬度和整個字的高度。
爲了便於比對空格,所有的空白位置都以句點符來代替。
要求輸入兩個整數m n,表示筆的寬度,X的高度。
思路:
觀察樣例發現X是對稱的,所以計算上面一半,下面一半倒着輸出就行了。
這題麻煩的點是他只給行數n,不給列數,要你自己算。
觀察樣例圖形可以發現,第n/2+1行星號的長度爲x,每向上一行就向左右拓展兩格
所以列數爲n/2*2+m。
後面的圖形打印就沒什麼好說的了。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e3+5;
char a[maxm][maxm];
signed main(){
int x,n;
while(cin>>x>>n){//x是X的寬度
int m=n/2*2+x;//列
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)a[i][j]=0;
int space=0;//前後的空格長度
for(int i=1;i<=n/2+1;i++){
for(int j=space+1;j<=space+1+x-1;j++){
a[i][j]='*';
}
for(int j=m-space;j>=m-space-x+1;j--){
a[i][j]='*';
}
space++;
}
for(int i=1;i<=n/2+1;i++){
for(int j=1;j<=m;j++){
if(!a[i][j])a[i][j]='.';
cout<<a[i][j];
}
cout<<endl;
}
for(int i=n/2;i>=1;i--){
for(int j=1;j<=m;j++){
cout<<a[i][j];
}
cout<<endl;
}
}
return 0;
}