11.8 总结
11.8 GZEZ NOIP2022模拟测试赛(五十六)
T1 环:
题目描述:对于一个0,1串有两种操作 :
- 整体向右移动x位
- 将某个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 互质
因为k与n互质,所以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 ;
}