A.生成树

思路:贪心。

一开始用写了个假算法,发现数据太弱了。
以第一棵树为基准,我们用记录每条边,规定,这样方便去重。

然后我们只需在第二课树找到第一棵树中不存在的边即可,因为第二棵树少的边和多的边数肯定相等的,不然边数和不可能为,所以根据贪心我们只需统计少的边即可,这样用就很方便了,此时的大小是:是相重的个数,

我们需要的答案是,所以答案是:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
set<PII>s;
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        if(u>v) swap(u,v);
        s.insert(make_pair(u,v));
    }
    int ans=0;
    for(int i=1;i<n;i++){
         int u,v;
        scanf("%d%d",&u,&v);
       if(u>v) swap(u,v);
        s.insert(make_pair(u,v));
    }
    printf("%d\n",s.size()-(n-1));
    return 0;
}

B.七彩线段

思路:状态压缩

看到最大只有,可以想到用一个数的二进制表示状态。

我们可以设个线段为状态的最大长度。

当不用第个线段时,显然有状态转移:

当用第个线段时,我们需要找到最后一个小于的线段,这一步可以排序后二分查找。

令找到的位置为

令新状态:

然后求的个数为个的最大值即可。

对于求二进制中的1可以递推求。

.

意思是删掉最右边的1.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int dp[N][130];
struct line{
    int l,r,c;
    bool operator<(const line&l)const{
        return r<l.r;
    }
}a[N];
int b[N],n,m,cnt[130];
void pre(int n){
    cnt[0]=0;
    for(int i=1;i<=n;i++) cnt[i]=cnt[i&(i-1)]+1;
}
int solve(){
    int ans=-1;
    memset(dp,-1,sizeof dp);
    dp[0][0]=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<128;j++){
            dp[i][j]=max(dp[i][j],dp[i-1][j]);
            int p=lower_bound(b+1,b+n+1,a[i].l)-b-1;
            if(~dp[p][j]){
            dp[i][j|(1<<(a[i].c-1))]=max(dp[i][j|(1<<(a[i].c-1))],dp[p][j]+a[i].r-a[i].l);
            //printf("dp[%d][%d]=%d\n",p,j,dp[p][j]);
            //printf("i=%d,j|x=%d\n",i,j|(1<<(a[i].c-1)));
            }
            if(cnt[j]==m) 
            ans=max(ans,dp[i][j]);
        }
    return ans; 
}
int main(){
    pre(128);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].c);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++) b[i]=a[i].r;
    printf("%d\n",solve());
    return 0;
}

C.成绩分析

思路:签到题,对于平均值,我们只需将所有数求和,然后除以即可,对于中位数,我们需要特判一下,如果是奇数,我们直接取中间的一个数,如果是偶数,就取中间的两个数的平均值,最后再将中位数和平均数,求绝对值差即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int a[N];
int main(){
    int n,b=0;
    cin>>n;
    for(int i=0;i<n;i++){
       cin>>a[i];
       b+=a[i];
    }
    b/=n;
    int x;
    if(n&1) x=a[n/2];
    else x=(a[n/2-1]+a[n/2])/2;
    printf("%d\n",abs(b-x));
    return 0;
}

D.刺客信条

思路:优先队列。

考虑:以到达时间较短的方格优先进入队列,然后从起点开始,把起点丢入队列,开始跑一遍,因为我们是以时间较短优先,所以最先到达终点的就是用时最少的了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=50+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
char a[N][N];
int n,m,d[4][2]={0,1,0,-1,1,0,-1,0},vis[N][N],b[N][N];
int sx,sy,ex,ey;
struct node{
    int x,y,d;
    bool operator <(const node& no)const{
        return d>no.d;
    }
}now;
int bfs(int x,int y){
    priority_queue<node>q;
    q.push({x,y});
    vis[x][y]=1;
    while(!q.empty()){
         now=q.top();q.pop();
         //printf("(%d,%d)=%d\n",now.x,now.y,now.d);
         if(now.x==ex&&now.y==ey) return now.d;
         for(int i=0;i<4;i++){
              int nx=now.x+d[i][0],ny=now.y+d[i][1];
             if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&!vis[nx][ny]){
                  int h=now.d+b[nx][ny];
                  vis[nx][ny]=1;
                 q.push({nx,ny,h});
             }
         }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
       for(int j=1;j<=m;j++){
           scanf("\n%c",&a[i][j]);
           if(a[i][j]=='S') sx=i,sy=j;
           else if(a[i][j]=='E') ex=i,ey=j;
           else if(a[i][j]=='A'||a[i][j]=='B'||a[i][j]=='C') b[i][j]=100;
           else b[i][j]=a[i][j]-'0';
       }
    printf("%d\n",bfs(sx,sy));
    return 0;
}

E.我不爱她

思路:

才发现是多校里的题目,之前太菜了不会,发现只需要代码改动一下就行了。

还是一样,考虑预处理所有字符串的后缀,用存起来,然后每个字符串作为前缀时的贡献,因为要满足最长的后缀也即最长的前缀,所以对于较短的重复的前缀我们要删去一些贡献,比如

对于来说,后缀的个数,但是实际上只有第一个字符串能与其匹配。

所以中的要减去较长的出现的前缀,即的个数为

所以的贡献实际上为个。

这一步可以用实现,因为中的数组就代表的最长前后缀的长度。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=998244353;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false),cin.tie(0)
string s[N];
int nt[N];
unordered_map<ull,int>mp;
int ans[N],base=233;
void kmp(string s){
    nt[0]=0;
    for(int i=1,j=0;i<s.size();i++){
        while(j&&s[i]!=s[j]) j=nt[j-1];
        if(s[i]==s[j]) j++;
        nt[i]=j;
    }     
}
void Hash(string s)    
{
    ull t=0,p=1;
    for(int i=s.size()-1;i>=0;i--){
        t+=p*(s[i]-'a'+1);
        p*=base;
        mp[t]++;
    }
}
int n;
int main(){
    IOS;
    int t;
    cin>>t;
    while(t--){
    cin>>n;
    mp.clear(),mst(ans);
    ll res=0;
    for(reg int i=0;i<n;i++)  cin>>s[i],Hash(s[i]);
    for(reg int i=0;i<n;i++){
        ull t=0;
        for(reg int j=0;j<s[i].size();j++){
            t=t*base+s[i][j]-'a'+1;
            ans[j]=mp[t];
        }
        kmp(s[i]);
        for(reg int j=0;j<s[i].size();j++){
             if(nt[j]) ans[nt[j]-1]-=ans[j];
        }
        for(reg int j=0;j<s[i].size();j++)
            res+=1LL*ans[j]*(j+1);
    }
    cout<<res<<endl; 
    }
    return 0;
}