2020牛客寒假演算法基礎集訓營1
分析:這題需要分3種情況來討論。
(1)2條邊都與座標軸平行的三角形,一定存在於\(2 \times 1\)或者\(1 \times 2\)的矩形中,每個當中存在4個,則只需要統計矩形的數量即可
(2)底與座標軸平行的三角形,且底為2
(3)底與座標軸平行的三角形,且底為1
#include "bits/stdc++.h" using namespace std; typedef long long LL; const LL mod=1e9+7; LL n,m; int main() { cin>>n>>m; LL sum=0; sum=(sum+(4ll*(n-1)*(m-2)%mod+(4ll*(m-1)*(n-2)%mod))%mod)%mod; sum=(sum+((2ll*(m-1)*(n-2)%mod*(n-2)%mod)%mod+(2ll*(n-1)*(m-2)%mod*(m-2)%mod)%mod)%mod)%mod; sum=(sum+((2ll*(m-2)*(n-1)%mod*(n-2)%mod)%mod+(2ll*(n-2)*(m-1)%mod*(m-2)%mod)%mod)%mod)%mod; cout<<sum<<endl; return 0; }
#include "bits/stdc++.h"
using namespace std;
int n,x,a,b;
int main()
{
scanf("%d%d%d%d",&n,&x,&a,&b);
int tmp=n*(x*a+((100-x)*b));
printf("%.2f\n",(double)tmp/100.0);
return 0;
}
分析:首先如果在同一象限內的,隔板無法分開。如果處於不同象限,我們則分別求出每個點與X軸和與Y軸的交點,然後使得擋板能夠擋住n-k個點即可,於是可以通過雙指標來求min值
#include "bits/stdc++.h" using namespace std; int n,k; int main() { double x0,y0; cin>>x0>>y0; cin>>n>>k; k=n-k; vector<double>v1,v2; for(int i=1;i<=n;i++){ double x,y; cin>>x>>y; if(x*x0<0){ v1.push_back(y0-x0*(y-y0)/(x-x0)); } if(y*y0<0){ v2.push_back(x0-y0*(x-x0)/(y-y0)); } } double mi=1e18; sort(v1.begin(),v1.end()); sort(v2.begin(),v2.end()); if(v1.size()>=k){ int head=0,tail=k-1; while(tail<v1.size()){ mi=min(mi,v1[tail]-v1[head]); head++,tail++; } } if(v2.size()>=k){ int head=0,tail=k-1; while(tail<v2.size()){ mi=min(mi,v2[tail]-v2[head]); head++,tail++; } } if(mi==1e18) printf("-1\n"); else printf("%.6f\n",mi); return 0; }
#include "bits/stdc++.h"
using namespace std;
const int maxn=1e5+100;
int n,a[maxn],vis[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++){
scanf("%d",&a[i]);
vis[a[i]]=1;
}
for(int i=1;i<=n;i++){
if(vis[i]==0){
printf("%d\n",i);
return 0;
}
}
}
#include "bits/stdc++.h"
using namespace std;
typedef long long LL;
LL n;
LL solve(LL a){
if(a==2ll) return 2ll;
LL tmp=2;
for(LL i=2;i*i<=a;i++){
if(i*i==a){
tmp++;
}else{
if(a%i==0) tmp+=2;
}
}
return tmp;
}
int main()
{
scanf("%lld",&n);
int cnt=0;
while(n!=2){
cnt++;
n=solve(n);
}
printf("%d\n",cnt);
return 0;
}
分析:如果我們以黑色結點為跟結點節點,則假設它的子樹中白色結點數量分別為\(x_i\),則總的方案數為\(\sum_{i=1}^{n}x_i+ \sum_{i=1}^{n} \sum_{j=1}^{n} x_i x_j\),化簡一下,即為\(\sum_{i=1}^{n}x_i + \frac {(\sum_{i=1}^{n}{x_i})^2-\sum_{i=1}^{n}{x_i}^2} 2\)
#include "bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e5+100;
char s[maxn];
int n;
vector<int>g[maxn];
LL sum;
void dfs(int u,int fa){
if(s[u]=='B') return;
sum++;
for(auto v:g[u]){
if(v!=fa) dfs(v,u);
}
}
int main()
{
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
LL ans=0;
for(int i=1;i<=n;i++){
if(s[i]=='B'){
LL res1=0,res2=0;
for(auto v:g[i]){
sum=0;
dfs(v,v);
ans+=sum;
res1+=sum;
res2+=sum*sum;
}
ans+=(res1*res1-res2)/2;
}
}
printf("%lld\n",ans);
return 0;
}
分析:依次記錄每個字母出現的位置,同時維護k個該字母長度的最小值。
#include "bits/stdc++.h"
using namespace std;
const int maxn=27;
vector<int>g[maxn];
int v[maxn];
int n,k;
string s;
int main()
{
cin>>n>>k;
cin>>s;
int len=s.length();
for(int i=0;i<26;i++) v[i]=INT_MAX;
for(int i=0;i<len;i++){
int tmp=s[i]-'a';
g[tmp].push_back(i+1);
if(g[tmp].size()>=k){
int l=g[tmp].size()-1;
int mi=g[tmp][l]-g[tmp][l-k+1]+1;
v[tmp]=min(v[tmp],mi);
}
}
int mx=INT_MAX;
for(int i=0;i<26;i++){
mx=min(mx,v[i]);
}
if(mx==INT_MAX) printf("-1\n");
else printf("%d\n",mx);
return 0;
}
分析:通過維護字首和來快速維護區間中1和0的數量,然後分別計算把0改為1和把1改為0所能維護的最長序列即可
#include "bits/stdc++.h"
using namespace std;
const int maxn=2e5+100;
int n,k,a[maxn],b[maxn],dp1[maxn],dp2[maxn];
string s;
int main()
{
scanf("%d%d",&n,&k);
cin>>s;
for(int i=1;i<=n;i++){
a[i]=s[i-1]-'0';
b[i]=1-a[i];
dp1[i]=dp1[i-1]+a[i];
dp2[i]=dp2[i-1]+b[i];
}
int l=1,r=1,mx=0;
while(r<=n&&l<=r){
while(dp1[r]-dp1[l-1]<=k&&r<=n) r++;
mx=max(mx,r-l);
//cout<<mx<<endl;
l++;
}
l=1,r=1;
while(r<=n&&l<=r){
while(dp2[r]-dp2[l-1]<=k&&r<=n) r++;
mx=max(mx,r-l);
//cout<<mx<<endl;
l++;
}
printf("%d\n",mx);
return 0;
}
分析:這個是一個比較簡單的DP,分別從"nico","niconi","niconiconi" 三個位置轉移過來求最大值即可
#include "bits/stdc++.h"
using namespace std;
const int maxn=3e5+10;
typedef long long LL;
int n;
LL a,b,c,dp[maxn];
string s;
int main()
{
cin>>n>>a>>b>>c;
cin>>s;
for(int i=1;i<=n;i++){
dp[i]=dp[i-1];
if(i>=4){
if(s.substr(i-4,4)=="nico") dp[i]=max(dp[i],dp[i-4]+a);
}
if(i>=6){
if(s.substr(i-6,6)=="niconi") dp[i]=max(dp[i],dp[i-6]+b);
}
if(i>=10){
if(s.substr(i-10,10)=="niconiconi") dp[i]=max(dp[i],dp[i-10]+c);
}
}
printf("%lld\n",dp[n]);
return 0;
}
分析:這個題目出得非常有意思,咋一看上去不知道怎麼矩陣快速冪,但仔細思考後,發現是可以構造矩陣進行快速冪的。在這裡我們應該\(x\)和\(y\)的冪次做矩陣快速冪。我們推一下\(x\)出現的次數,可知其實一個前2項為\([1,0]\)的斐波拉契數列。同理,我們可以推出\(y\)的出現次數是一個前2項為\([0,1]\)的斐波拉契數列。我們再做進一步推理可以發現,\(x\)的第\(n\)項\(f(n)\),就是\(y\)的第\(n-1\)項\(f(n-1)\)。同時,\(a^b\)的第n項就是\(f(n)+f(n-1)-1\)。最後由於指數比較大,同時右由於\(10^9+7\)是個素數,所以我們很自然可以想到用費馬小定理來進行降冪,即當\(p\)為素數時,\(a^{p-1}=1(mod p)\)。
#include "bits/stdc++.h"
using namespace std;
typedef long long LL;
const LL mod=1e9+7;
LL n,x,y,a,b;
void mul(LL f[2],LL res[2][2]){
LL c[2];
memset(c,0,sizeof(c));
for(int j=0;j<2;j++){
for(int k=0;k<2;k++)
c[j]=(c[j]+f[k]*res[k][j])%(mod-1);
}
memcpy(f,c,sizeof(c));
}
void mulself(LL res[2][2]){
LL c[2][2];
memset(c,0,sizeof(c));
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
for(int k=0;k<2;k++){
c[i][j]=(c[i][j]+res[i][k]*res[k][j])%(mod-1);
}
}
}
memcpy(res,c,sizeof(c));
}
LL quick(LL a,LL b){
LL res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res%mod;
}
int main()
{
cin>>n>>x>>y>>a>>b;
if(n==1){
cout<<x%mod<<endl;
return 0;
}
if(n==2){
cout<<y%mod<<endl;
return 0;
}
if(x%mod==0||y%mod==0||a%mod==0){
cout<<"0"<<endl;
return 0;
}
x%=mod,y%=mod,a%=mod;
a=quick(a,b);
LL f[2]={1,0};
LL res[2][2]={{0,1},{1,1}};
n--;
for(;n;n>>=1){
if(n&1) mul(f,res);
mulself(res);
}
LL tmp1=quick(x,f[0]);
LL tmp2=quick(y,f[1]);
LL tmp3=quick(a,f[0]+f[1]-1);
cout<<((tmp1*tmp2)%mod*tmp3)%mod<<endl;
return 0;
}