寒假训练2021-1-29

昨天太累了 - - cf 也没打好 头疼 今早重现做了一遍
1-28 组队训练
在这里插入图片描述
B题 dp
题意有点冗杂,思索了很久感觉没法贪心,应该是个dp ,但是对于第一关和第二关的经验限制又想不到好的对策。如果我们以第一关经验为0 出发,就算可以取到最优的策略,但是对第二关是有影响的。而且每个怪物分不同关打的时候可以获得不同的exp以及花费不同的time 那么 问题就在于确定这个怪在第几关打最优 。

分析:
大概确定了要枚举第i个怪物,然后由于要先满足第一关后满足第二关,那么就是套个枚举第一关的经验值exp1 和第二关的经验值exp2
那么此时转移方程就很显然了
在这里插入图片描述

在这里插入图片描述
经验值我们要从大到小枚举,此外,为了线性的转移上去,对怪物的第一关的获得的经验sort一下就OK

ll dp[600][600];
struct pe{
    
    
ll v1,t1,v2,t2;

}boss[1111];
bool cmp(pe x,pe y){
    
    
return x.v1<y.v1;
}
signed main(){
    
    
ll n,s1,s2;
read(n);
read(s1);
read(s2);
for(int i=1;i<=n;i++){
    
    
    read(boss[i].v1);
    read(boss[i].t1);
    read(boss[i].v2);
    read(boss[i].t2);
}
sort(boss+1,boss+1+n,cmp);
ll inf=1e12;
for(int i=0;i<=s1;i++){
    
    
    for(int j=0;j<=s2;j++){
    
    
        dp[i][j]=inf;
    }
}
dp[0][0]=0;
for(int i=1;i<=n;i++){
    
    
   for(int j=s1;j>=0;j--){
    
    
    for(int k=s2;k>=0;k--){
    
    
            if(dp[j][k]>=inf){
    
    continue;}
    if(j<s1){
    
    
    // 第一关的经验不够
    ll now_s1=j+boss[i].v1;
    ll now_s2=k;
    if(now_s1>s1){
    
    
    // 溢出
    now_s2=min(s2,now_s2+(now_s1-s1));// 获得溢出经验
    now_s1=s1;
    }
    dp[now_s1][now_s2]=min(dp[now_s1][now_s2],dp[j][k]+boss[i].t1);
    }
    if(k<s2){
    
    
     ll now_s1=j;
     ll now_s2=min(k+boss[i].v2,s2);
     dp[now_s1][now_s2]=min(dp[now_s1][now_s2],dp[j][k]+boss[i].t2);


    }

    }
   }
}
if(dp[s1][s2]>=inf){
    
    
    printf("-1");
    return 0;
}
    printf("%lld",dp[s1][s2]);
}

D题 字符串构造,思维模拟
给了我们一个字符串,问能不能重新组合一下,使得该字符串不存在任何相同的长度为n的子串 (字符串长度2n) 字符串围成一个圈
分析:
先记录一下每个字母的数量来观察一下 如果存在一个字符的数量>=2 * n -1 是一定不可以的 再来分析 一个字母出现了2
n - 2次 aabb
另外剩下的两个字母是相同的,但是它却是yes 再来分析一下,如果最多的字母的数量小于等于n 那么在一个环里面,他就一定是合法的 可以直接输出该字符串。 另外的就是处理这种2 * n -2 个相同字符的情况了,剔除了n=2, 不难发现剩下的 n 的取值 如果剩下的两个字母是相同的,那么一定无解。
下面来讨论一种针对2*n-2 的构造方法, 如果先拿出来 n 个字符 放一个数量为1的字符,剩下的再放最多的字符,再放最后一个字符 (思索) 妙

#include<bits/stdc++.h>
#include<stdlib.h>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define ll long long
#define int long long
#define inf 0x3f3f3f3f
#define mods 1000000007
#define modd 998244353
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x&(-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
#define IOS ios::sync_with_stdio(false);cin.tie(0)
 using namespace std;
 ll gcd(ll a,ll b){
    
    if(a<0)a=-a;if(b<0)b=-b;return b==0?a:gcd(b,a%b);}
template<typename T>void read(T &res){
    
    bool flag=false;char ch;while(!isdigit(ch=getchar()))(ch=='-')&&(flag=true);
for(res=ch-48;isdigit(ch=getchar());res=(res<<1)+(res<<3)+ch - 48);flag&&(res=-res);}
ll lcm(ll a,ll b){
    
    return a*b/gcd(a,b);}
ll qp(ll a,ll b,ll mod){
    
    ll ans=1;if(b==0){
    
    return ans%mod;}while(b){
    
    if(b%2==1){
    
    b--;ans=ans*a%mod;}a=a*a%mod;b=b/2;}return ans%mod;}//快速幂%
ll qpn(ll a,ll b, ll p){
    
    ll ans = 1;a%=p;while(b){
    
    if(b&1){
    
    ans = (ans*a)%p;--b;}a =(a*a)%p;b >>= 1;}return ans%p;}//逆元   (分子*qp(分母,mod-2,mod))%mod;

char s[1111111];
ll vis[30];
signed main(){
    
    
scanf("%s",s+1);
ll n=strlen(s+1);

ll ma=0;
for(int i=1;i<=n;i++){
    
    
   ll pos=s[i]-'a'+1;
   vis[pos]++;
   ma=max(ma,vis[pos]);
}
n=n/2;
if(ma<=n){
    
    
  printf("YES\n");
  for(char i='a';i<='z';i++){
    
    
   ll op=vis[i-'a'+1];
   for(int j=1;j<=op;j++){
    
    
   printf("%c",i);

   }

  }
return 0;

}
if(ma>=2*n-1){
    
    
    printf("NO");
    return 0;
}


if(ma==2*n-2){
    
    
 ll pos=0;
 for(char i='a';i<='z';i++){
    
    

    if(vis[i-'a'+1]==2){
    
    
    printf("NO");
    return 0;
    }
  if(vis[i-'a'+1]==2*n-2){
    
    
    pos=i-'a'+1;
  }
 }
 //-----------------   YES
 printf("YES\n");
 for(int i=1;i<=n;i++){
    
    
  printf("%c",'a'+(pos-1));

 }
 vis[pos]-=n;
 for(char i='a';i<='z';i++){
    
    
    if((i-'a'+1)!=pos&&vis[i-'a'+1]!=0){
    
    
     printf("%c",i);
     vis[i-'a'+1]--;
     break;
    }

 }
 for(int i=1;i<=vis[pos];i++){
    
    
   printf("%c",'a'+(pos-1));

 }
 vis[pos]=0;

 for(char i='a';i<='z';i++){
    
    
    ll op=i-'a'+1;
    for(int j=1;j<=vis[op];j++){
    
    
        printf("%c",i);
    }

 }

 return 0;

}
//------------  YES

printf("YES\n");
ll pos=0;
for(char i='a';i<='z';i++){
    
    
   ll op=i-'a'+1;
   if(vis[op]>n){
    
    
   pos=i-'a'+1;
   }

}

for(int i=1;i<=n;i++){
    
    
    printf("%c",'a'+(pos-1));
}
vis[pos]-=n;

for(char i='a';i<='z';i++){
    
    
    ll op=i-'a'+1;
    if(op!=pos&&vis[op]!=0){
    
    
        vis[op]--;
        printf("%c",i);
        break;
    }

}
for(int i=1;i<=vis[pos];i++){
    
    
    printf("%c",'a'+(pos-1));
}
vis[pos]=0;
for(char i='a';i<='z';i++){
    
    
    ll op=i-'a'+1;
    for(int j=1;j<=vis[op];j++){
    
    
        printf("%c",i);
    }

}


}

F 树上博弈
套路题 从根开始搜索 求匹配度


vector<ll>G[111111];
ll dfs(ll s,ll rt){
    
    
    ll ans=0;
for(int i=0;i<G[s].size();i++){
    
    
   if(G[s][i]==rt){
    
    continue;}

   ans=ans+dfs(G[s][i],s);

}
if(!ans){
    
    return 1;}
return ans-1;
}
signed main()
{
    
    
 ll n;
 read(n);
 for(int i=1;i<=n-1;i++){
    
    
  ll u,v;
  read(u);
  read(v);
  G[u].push_back(v);
    G[v].push_back(u);

 }  //  从根节点 
  ll flag=dfs(1,0);
  if(flag){
    
    
    printf("Alice");
    return 0;
  }
  printf("Bob");

}

剩下的I J 签到题 没啥写的 - -
cf 698div2 昨晚头疼,打的很烂 ,也确实不会做。 A题没啥好说的统计最多的数字,B题 不知道为什么>= 10d一定是yes。 C题就是根据di 推式子然后模拟过去cheak
在这里插入图片描述
推出来就是这个式子 因为题意是 对于任意的ai 都有它的负数存在,那么假设给这个原始a数组分成两部分,1–n 正整数且从小到大排列, n+1–2n就放他们的负数。那么不妨先计算一下d1 ---- dn
根据我草稿纸上面推的那样,得到 di 关于 ai i 以及前缀后缀的表达式子,对于di 相邻做差可以得到ai 与 ai+1 的关系 那么思路就已经浮现了,说到这里应该都会写了

ll d[222222];
map<ll,ll>s;
vector<ll>vis;
map<ll,ll>sb;
signed main()
{
    
    
    ll t;
    read(t);
    while(t--)
    {
    
    
        ll n;
        read(n);
        s.clear();
        sb.clear();
        vis.clear();
        for(int i=1; i<=2*n; i++)
        {
    
    
            read(d[i]);
            s[d[i]]++;
        }
        sort(d+1,d+1+2*n);
        ll f=0;
        ll num=0;
        for(int i=1; i<=2*n; i++)
        {
    
    
            if(s[d[i]]!=2)
            {
    
    
                f++;
                break;
            }
            else
            {
    
    
                if(sb[d[i]]==0)
                {
    
    
                    if(d[i]%2!=0)
                    {
    
    
                        num++;
                    }
                    vis.push_back(d[i]);
                    sb[d[i]]++;
                }
            }

        }

        if(f||vis.size()!=n||num!=0)
        {
    
    
            printf("NO\n");
            continue;
        }
        ll gg=0;
        ll rnm=vis[0]; // sum *2
        ll res=0;
        ll opp=0;
        for(int i=0; i<vis.size()-1; i++)
        {
    
    

            ll ok=vis[i+1]-vis[i];
            if(ok%(2*(i+1))!=0||ok<=0)
            {
    
    
                gg++;
                break;
            }
            ll cnt=ok/(2*(i+1));
            res+=opp+cnt;
            opp=opp+cnt;
            if(cnt<=0)
            {
    
    

                gg++;
                break;
            }

        }
        if(gg||(rnm/2-res)<=0||(rnm/2-res)%n!=0)
        {
    
    
            printf("NO\n");
            continue;
        }
        else
        {
    
    
            printf("YES\n");
        }
    }


}

至于D题为什么是 k -a[i] % gcd 就有解 - - 玄学 难顶 ,不会证明

在这里插入图片描述
简单最短路+枚举 暴力就好了


const int manx=1e4+500;
const int mamx=4e5+5;
ll head[manx],d[manx],hurt[manx];
bool vis[manx];
ll k=0,s,e,kk,ans;
struct node{
    
    
    ll v,next,w;
}a[mamx];
void add(ll u,ll v,ll w)
{
    
    
    a[++k].next=head[u];
    a[k].w=w;
    a[k].v=v;
    head[u]=k;
}
map<pair<ll ,ll >,ll >dis;
void dij()
{
    
    
    memset(d,inf,sizeof(d));
    memset(vis,0,sizeof(vis));
    d[s]=0;
    priority_queue<pair<ll,ll> >q;
    q.push(mp(0,s));
    while(q.size()){
    
    
        ll u=q.top().se;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=a[i].next){
    
    
            ll v=a[i].v,w=a[i].w;
           if(d[v]>d[u]+w){
    
    
          //printf("qwq %lld\n",d[v]);      //松弛操作,更新距离
            d[v]=d[u]+w;
            dis[{
    
    s,v}]=d[v];
           // printf("sad %lld %lld %lld %lld\n",d[u],u,res,d[v]);
            q.push(mp(-d[v],v)); //把更新的距离和点入队,这里距离取负变成小根堆
            }

        }
    }
}
ll pos[60];

map<ll,ll>rnm;
signed main()
{
    
       ll nu;
ll n,m;
    read(n);
    read(m);
    read(nu);
    //e=n,s=1;//s代表最短路的起源点
    for(int i=1;i<=nu;i++){
    
    
        read(pos[i]);
        rnm[pos[i]]=1;
    }
    for(int i=1;i<=m;i++)
    {
    
    
       ll u,v,w;
       read(u);
       read(v);
       read(w);
        add(u,v,w);
        add(v,u,w);
    }
    sort(pos+1,pos+1+nu);
    for(int i=1;i<=nu;i++){
    
    
    s=pos[i];
    dij();
    }

    ll mi=1e18;

    do{
    
    

    for(int i=1;i<=n;i++){
    
    

  if(rnm[i]){
    
    continue;}

    ll cnt=0;
    for(int k=1;k<=nu-1;k++){
    
    
     cnt+=dis[{
    
    pos[k],pos[k+1]}];

    }
    cnt=cnt+dis[{
    
    pos[1],i}]+dis[{
    
    pos[nu],i}];
    mi=min(mi,cnt);

    }
    }while(next_permutation(pos+1,pos+1+nu));
    printf("%lld",mi);
    return 0;
}

补题哦 ! 每次打完比赛都要认真的补题

猜你喜欢

转载自blog.csdn.net/weixin_45948940/article/details/113388424
今日推荐