2018青岛区域赛训练总结及cde题解
这场一开始st卡在了IO的上面T了两发,我wa了两发c,有一些情况没有考虑到。50mins的时候我看st代码,发现没问题大胆换了IO,AC,跟st讲了2minsC的时候发现了wa点,改了AC。随后cxy也AC了J题。大概过了快1小时,我们做不下去了,退场补题,发现DE还是可以做的,对标区域赛铜尾,如果DE开出来可能能进到银尾。
总结:
- IO必须开。
- 现场赛签到还是和队友讨论后交。
- E题没有耐心想下去。
M J是队友写的,貌似也是签到不写了。
题解:
C
题意:给你2个01串,你可以选择两个区间将区间内的所有数0变1,1变0。问有几种方法把他变为一样。卡O1
思路:
如果有两个联通块区间不一样那么方案数为6。
如果都一样那么方案数为(n+1)*n/2
如果只有1个,那么方案数为不一样的个数(cnt-1)2+一样的个数cnt22
此外0
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int inf = 0x3f3f3f3f;
const ll INF =0x3f3f3f3f3f3f3f3f;
int main(){
IO;
int t;cin>>t;
while(t--){
int n;cin>>n;
string s,t;cin>>s>>t;
vector<int>a;
forn(i,n){
if(s[i]==t[i]) a.push_back(0);
else a.push_back(1);
}
int id = 0;
forn(i,n)if(a[i]){
if(i&&a[i-1]);
else id++;
}
if(id>2){
cout<<0<<'\n';
}else if(id==2){
cout << 6<<'\n';
}else if(id==1){
int cnt = -1,cnt2 = 0;
forn(i,n){
if(a[i]) cnt++;
else cnt2++;
}
cout<<2*cnt+2*cnt2<<'\n';
}else cout<<1ll*n*(1+n)/2<<'\n';
}
return 0;
}
E
题意:从1点出发每到一个点这个点的权值增长ai,问走了m次后,让所有点权值最小值最大。
思路:典型的二分题,只是被题意蒙蔽了双眼,想了个其他的二分,不想写了。
二分ans,主要judge比较坑难写。
我们先算出每个点到达lim需要访问至少几次,然后那么ai走了5次,ai+1要至少走5次。
所以每个点走的次数就是x(当前点至少访问次数)+x-1(需要消耗来回拐弯)。加一些特判和修改就写成了代码的样子。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i, n) for (int i = 0; i < n; i++)
#define for1(i, n) for (int i = 1; i <= n; i++)
#define IO \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0)
const int maxn = 1e5 + 5;
ll a[maxn], n, m;
bool check(ll lim) {
ll sum = 0, x = 0, y = 0;
forn(i, n) {
x = (lim - 1) / a[i] + 1;
x -= y;
if (x <= 0) {
if (i != n - 1)
x = 1;
else
x = 0;
y = 0;
} else
y = x - 1;
sum += x + y;
if (sum > m) return 0;
}
return 1;
}
int main() {
IO;
int t;
cin >> t;
while (t--) {
cin >> n >> m;
forn(i, n) cin >> a[i];
ll l = 1, r = 1e17 + 1;
while (l <= r) {
ll mid = l + r >> 1;
if (check(mid))
l = mid + 1;
else
r = mid - 1;
}
cout << l - 1 << '\n';
}
return 0;
}
D.
题意:给一串数字,他由下图构成。这里的+号代表放在后面。问构成这串数字最小字典序的AB是多少。
思路:
想明白一点,如果x = ai*bi,那么x肯定>ai||>bi。所以我们枚举a0的9种可能来爆搜判断。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int maxn = 2e5+5;
int n,m,pos,a[maxn],b[maxn],len;char s[maxn];
bool getb(){
forn(i,m){
int x = s[pos++] - '0';
if(a[0]>x&&x) x = x*10+s[pos++]-'0';//cerr<<x<<' '<<a[0]<<'\n';
if(x%a[0]==0&&x/a[0]<10) b[i] = x/a[0];
else return 0;
}
return 1;
}
bool geta(){
for1(i,n-1) {
int x = s[pos++] - '0';
if(b[0]>x&&x) x = x*10+s[pos++]-'0';
if(x%b[0]==0&&x/b[0]<10) a[i] = x/b[0];
else return 0;
for1(j,m-1) {
int x = s[pos++] - '0';
if(a[i]>x&&x) x = x*10+s[pos++]-'0';
if(a[i]*b[j]!=x) return 0;
}
}
return 1;
}
bool solve(){
int one = s[0]-'0';
len = strlen(s);
for1(i,9)if(one%i==0){
pos = 0;
a[0] = i;
if(getb()&&geta()&&pos==len)return 1;
}
int two = one*10+s[1]-'0';
for1(i,9)if(two%i==0&&two/i<10){
pos = 0;
a[0] = i;
if(getb()&&geta()&&pos==len)return 1;
}
return 0;
}
int main(){
IO;
int t;cin>>t;
while(t--){
cin>>n>>m;
cin>>s;
if(solve()){
forn(i,n) cout<<a[i];
cout<<' ';
forn(i,m)cout<<b[i];
cout<<'\n';
}else cout<<"Impossible"<<'\n';
}
return 0;
}
人一我百,人十我万。