【五一qbxt】test1

(不知道爲何竟然爆零了qwq)html

(全員爆零誒,最高分10分???仍是rand出來的???)ios

我freopen寫錯了????自閉了c++

不行不行再寫一遍freopen加深印象,不能再寫錯了編程

freopen("文件名","r",stdin);
freopen("文件名","w",stdout);

行吧,仍是來整一整老師給的題解吧qwq數組

忍不住bibi一句出題的這個哥哥好年輕啊qwq,我都很差意思叫人家老師,應該是哥哥編程語言


 

題目ui

Problem A.最近公共祖先spa

先列一個表格3d

 這樣大概就理解71是怎麼出來的了code

30pts思路:

    將整棵樹建出,共有 O(Kn) 個節點,計算任意兩個節點的LCA 複雜度 O(n)。

    注意到沒有必要將全部點對的 LCA 都計算出來, 由於對於同一層的兩個節點 u 和 v,

T depth(lca(i, u)) 和 ∑ Tdepth(lca(i, v)) 是相同的, 每一層只須要算一個節點就行, 時間複雜 度 O(n2K n) 。

(以上是老師的思路,可是我如今對建樹這種事有點蒙???當時考試的時候爆零了qwq,實際上個人暴力也是能夠得30分的。下次必定得記得開long long

先帶上一個七十多行的大暴力:

30分代碼:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define mo 998244353 using namespace std; long long k,n,ans,s,z; int t[1000010]; struct c{ int num1,num2; //num1表示以這一層爲根的子樹的結點數 //num2表示這一層的結點數 }ceng[1000010]; int quick_pow(int a,int b) { //快速冪 int ans=1; if(b==0)return 1; while(b){ if(b&1)ans=(a*ans)%mo; a=(a*a)%mo; b/=2; } return ans%mo; } int num1(int n,int k){//求子樹點數 n層k叉樹 int z=n-1; int num=0; while(z){ num+=quick_pow(k,z); z--; } return num+1; } void ych(){ for(int i=1;i<=n;i++){ ceng[n-i+1].num1=num1(i,k);//以這一層爲根的子樹的結點數 ceng[i].num2=quick_pow(k,i-1);//這一層的結點數 } } int main(){ scanf("%d%d",&n,&k); ych();//他原本叫作預處理,後來被我改爲了於才鴻 for(int i=1;i<=n;i++) ans+=num1(n-i+1,k)*quick_pow(k,i-1)*i;//求的是子樹的 //這裏算的是某個點如下的depth之和包括它本身 for(int i=1;i<=n;i++) s+=i*quick_pow(k,i-1);//這裏算的是每一個點本身的depth和 ans-=s; ans=ans*2%mo; for(int i=2;i<=n;i++){ for(int j=1;j<i;j++){ z+=(k-1)*ceng[i-j+1].num1*(i-j)*quick_pow(k,i-1); //算的是同一深度的 } } ans+=z; ans%=mo; cout<<(ans+s)%mo<<endl; return 0; }

 60pts思路:

更通常的,題目計算的是任意兩個點 LCA 的深度,這個數量等於,任意兩個點公共的祖先共有多少個。考慮計算第 i 層的某個節點,是多少個點對的公共祖先,這個數量就是該節點子樹內節點數的平方。第 i 層的節點所在子樹節點數 1+K+···+K n−i=(Kn−i+1−1)/(K−1) 。答案就是 n i=1 ( (Kn−i+1−1)/(K−1) ) 2 ∗ K i−1

即:

求任意兩點LCA深度=>任意兩點公共祖先數量=>a是多少個點對的公共祖先(枚舉對於每一個數a,它是x,y的公共祖先=>x,y在a的子樹裏。有多少個點在a的子樹中呢?)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
const int mo=998244353;

using namespace std;

long long ans,k1;

int n;

long long pow(long long a,int b){
    long long ans=1;
    if(b==0)return 1;
    while(b){
        if(b&1)ans = (a*ans)%mo;
        a = (a*a)%mo;
        b/=2;
    }
    return ans%mo;
}

int main(){
    scanf("%d%lld",&n,&k1);
    
    for(int i=1;i<=n;i++)
        ans=(ans+pow((long long)(pow(k1,n-i+1)-1)%mo*pow(k1-1,mo-2)%mo,2)*pow(k1,i-1))%mo;
//核心式子的編程語言
        
    cout<<ans%mo<<endl;
}

100pts思路:

#include <bits/stdc++.h>

using namespace std;

const int mod = 998244353;
typedef long long LL;
int fpm(int p, int k)
{
    int res = 1;
    for (p %= mod; k; k >>= 1, p = (LL) p * p % mod)
        if (k & 1) res = (LL) res * p % mod;
    return res;
}
int main()
{
//    freopen("lca.in", "r", stdin);
//    freopen("lca.out", "w", stdout);
    int n, K; cin >> n >> K;
    int e = fpm(K - 1, mod - 2); //e=1/(K-1)
    int x = (fpm(K, n) - 1) * (LL) e % mod; //x=(K^n-1)/(K-1);
    int ans = (fpm(K, n + 1) + 1) * (LL) x % mod;//ans=(K^(n+1)+1)*((K^n-1)/(K-1))
    ans = (ans - 2 * n * (LL) fpm(K, n)) % mod;//ans=(K^(n+1)+1)*((K^n-1)/(K-1))-2nK^n;
    ans = ans * (LL) e % mod * (LL) e % mod;//ans*=(1/(K-1))^2;
    cout << (ans < 0 ? ans + mod : ans);//三目運算符,判斷是否爲負數 
}

Problem2:最長公共迴文子序列:

50pts思路:

經典的作法:

設s,t的反串(eg:abbsf 反串fsbba)爲sr,tr,那麼求s和t的最長公共迴文序列就是求s,sr,t,tr的最長公共子序列。


 

如何求最長公共子序列?

用DP!

怎麼用?

粘一篇別人的博客吧:連接

對於二維的轉移方程:

dp[i][j]=   dp[i-1][j-1]   s[i]=t[j];

               max{dp[i-1][j],dp[i][j-1]}  s[i]!=t[j];

對於四維的:

 

用一個DP來作就能夠獲得50pts了:

 然鵝我……寫炸了。

#include<bits/stdc++.h>

using namespace std;

int s[101],t[21],sr[101],tr[21];
int len_s,len_t,max1,max2;
int dp[101][51][101][51];
char s1[100],t1[21];

void scl(){
    for(int i=0;i<len_s;i++){
        sr[len_s-i]=s1[i];
        s[i+1]=s1[i];
    }
    for(int i=0;i<len_t;i++){
        tr[len_t-i]=t1[i];
        t[i+1]=t1[i];
    }
}

int main(){
    scanf("%s",s1);
    scanf("%s",t1);
    len_s=strlen(s1);
    len_t=strlen(t1);
    scl();
    for(int i=1;i<=len_s;i++){
        for(int j=1;j<=len_t;j++){
            for(int k=1;k<=len_s;k++){
                for(int l=1;l<=len_t;l++){
                    if(s[i]==sr[k]&&s[i]==t[j]&&s[i]==tr[l]&&sr[k]==t[j]&&sr[k]==tr[l]&&t[j]==tr[l])
//注意這裏不能略寫爲s[i]==t[j]==sr[k]==tr[l]{ dp[i][j][k][l]=dp[i-1][j-1][k-1][l-1]+1; } else { max1=max(dp[i-1][j][k][l],dp[i][j-1][k][l]); max2=max(dp[i][j][k-1][l],dp[i][j][k][l-1]); dp[i][j][k][l]=max(max1,max2); } } } } } cout<<dp[len_s][len_t][len_s][len_t]<<endl; }

 

100pts:

暴力:一位一位的遍歷,On*2m

 

顯然很慢啊qwq;

加速???

維護一個26*n的數組,記錄距離某個位置最近的某個字母(26個字母)在哪兒;

不會寫qwq自閉

Problem3:

直接講100pts的貪心:

這個題的原理忘記爲啥了???

即解一個二元一次方程組13a+5b=t。

因此咱們分別枚舉a,b找到最小的t;

複雜度O(30n)

相關文章
相關標籤/搜索