11.8 总结

11.8 GZEZ NOIP2022模拟测试赛(五十六)

T1 环:

题目描述:对于一个0,1串有两种操作 :

    1. 整体向右移动x位
    1. 将某个01变成10

给你串的长度和1的个数让你构造l个串 , 满足s[i]可以即经过第一种也可以经过第二种
操作变s[i+1]

Solution

我们先考虑是否有解:
若n,k互质则有解 , 证明:
\(W_i\)为串中1的下标和
经过第一种操作\(W_{i+1}= W_i + x*k (mod n )\)
经过第二种操作\(W_{i+1}=W_i+1(mod n)\)
所以若有解则\(x * k \equiv 1\) 德x , k 互质
因为kn互质,所以k一定存在 \(\bmod n\)意义下的逆元,令s[0]\(\frac{0}{k}\),\(\frac{1}{k}, \cdots, \frac{k-1}{k}\) 位置处为1 , 其余处为0, 同样的对于\(s[i]\),我们令其\(\frac{i}{k}, \frac{i+1}{k}, \cdots, \frac{k+i-1}{k}\)处为1,其余处为0。显然,从\(s[i]\)变成\(s[i+1]\)可以通过向右循环移位\(\frac{1}{k}\)实现,同 时发现\(\frac{i}{k}+1=\frac{k+(i+1)-1}{k}\),于是我们将\(s[i]\)\(\frac{i}{k}\)位置变成 0,将 \(\frac{i}{k}+1\)位置变成1即可用b操作将\(s[i]\)变成\(s[i+1]\)

Code:

#include<bits/stdc++.h>
using namespace std ;
const int SIZE = 105 ;

char s[SIZE] ;

int n , k , l ;

int exgcd(int a , int b , int &x , int &y){
   if(!b){
      x = 1 ;
      y = 0 ;
      return a ;
   }
   int d = exgcd(b , a % b , y , x) ;
   y -= (a / b * x) ;
   return d ;
}

inline int inv(int a , int b){
   int x , y ;
   exgcd(a , b , x , y) ;
   return (x % b + b) % b ;
}

int main(){
   int T ; cin >> T ;
   while(T-->0){
      cin >> n >> k >> l ;
      if(__gcd(n , k) != 1){
         cout << "NO" << endl ;
         continue ;
      }  
      else cout << "YES" << endl ;
      int iv = inv(k , n) ;
      
      for(int i = 1 ; i <= l ; i += 1){
         memset(s , '0' , sizeof s) ;
         for(int j = 0 ; j < k ; j += 1){
            s[(i + j) * iv % n] = '1' ;
         }
         
         s[n] = '\0';
         cout << s << endl ;
      }
   }
}

T2:DNA序列

题目描述:

给你n个字符串其中只包含"A , T , C , G" ,我们要取每个字符串的一个非空前缀然后随意> 拼接使得字典序最小

Solution:

正解:https://yhx-12243.github.io/OI-transit/records/uoj494.html

乱搞:随机化shuttfle\

Code

Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 55;
int n;bool pre[N];
string s[N],t[N];
string solve(int x)
{
    if(x==n) return s[x].substr(1,1);
    string ans = solve(x+1);
    vector<string>tmp;
    for(int i=1;i<s[x].size();i++)
    {
        tmp.emplace_back(s[x].substr(1,i)+ans);
    }
    sort(tmp.begin(),tmp.end());
    return tmp[0];
     
}
mt19937 rd(time(0));
mt19937 rdd(time(0));
int main()
{
    cin >> n; int i;
    for(i=1;i<=n;i++) cin >> s[i],s[i] =" " + s[i];
    string ans = solve(1);int T = 4200;
    sort(s+1,s+n+1);
    ans = min(ans,solve(1));
    while(T-->0){
    	int l = rd()%n+1;
    	int r = rd()%n+1;
    	swap(s[l],s[r]);
    	string  now = solve(1);
    	if(now > ans) swap(s[l],s[r]);
        else ans = now;
    }
    cout << ans << '\n';
    return 0;
}

T3探寻

题面描述:

给你一张图,每个点都有一个权值表示当到达这个点时所获得的收益,每条边有一个边权表示要经>过这条边的花费(花费和收益只能用一次),起点是0,问到达目标点的最小起始花费

Solution:

map上跑启发式合并
先按既定条件排序:
价值 = 收益 - 代价

  • 1.价值为正 , 价值大的比价值小的优先级靠前
  • 2.价值为负 ,代价小的优先
  • 3.价值为负,代价相同,收益大的有限

Code:

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int SIZE = 2e5 + 20 ;
const ll inf = 1e18 ;
#define pa pair<ll,ll> 
#define fi first 
#define se second
#define ma(x,y) make_pair(x,y)

ll w[SIZE] , v[SIZE] ;
int n ;
multiset<pa> s[SIZE] ;
vector<int> g[SIZE] ;

void dfs(int now){
    for(auto v : g[now]){
        dfs(v) ;
        if(s[now].size() < s[v].size()) swap(s[now] , s[v]) ;
        s[now].insert(s[v].begin() , s[v].end()) ;
        s[v].clear() ;
    }
    
    ll tw = w[now] ;
    ll tv = v[now] - w[now] ;
    
    while(!s[now].empty() && (tv <= 0 || tw >= s[now].begin()->first)){
        auto tmp = *s[now].begin() ;
        tw += max(0ll , tmp.first-(tw+tv)) ;
        tv += tmp.se ;
        s[now].erase(s[now].begin()) ;
    }
    if(tv > 0) s[now].insert(ma(tw,tv)) ;
    else s[now].clear() ;
}

int main(){
    cin >> n ;
    w[1] = inf ;
    for(int i = 2 ; i <= n ; i += 1){
        int x ; cin >> x ;
        cin >> v[i] >> w[i] ;
        g[x+1].emplace_back(i) ;
        if(v[i] == -1) v[i] = inf * 2 ;
    }
    dfs(1) ;
    cout << s[1].begin() -> first - inf ;
}
posted @ 2022-11-08 18:43  Guier-Lime  阅读(11)  评论(0编辑  收藏  举报