第一场
09String
感觉就是银行家算法。。。。赛时没看不过看了也不一定能出,因为很多细节要考虑。
思路就是对于每一位贪心的试探,验证一下放进之后能不能还能构成答案,,如果能的话就放进去,不能就撤销。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m;
struct node{
int l, r;
}s[30];
char ss[maxn], res[maxn];
pair<vector<int>, int>ch[30];
int num[maxn][30], use[30];
void init() {
for(int i = 0; i <= 26; i++) {
ch[i].first.clear();
ch[i].second = 0;
use[i] = 0;
}
for(int i = 0; i <= n+1; i++) {
for(int j = 0; j <= 26; j++) num[i][j] = 0;
}
}
void solve() {
int k;scanf("%d", &k);
n = strlen(ss+1);
init();int last = 0;
for(int i = 0; i <= 26; i++)
{scanf("%d%d", &s[i].l, &s[i].r);}
for(int i = 1; i <= n; i++)
ch[ss[i]-'a'].first.push_back(i);
for(int i = n; i >= 1; i--)
for(int j = 0; j <= 25; j++)
if(j == ss[i]-'a') num[i][j] = num[i+1][j]+1;
else num[i][j] = num[i+1][j];
for(int i = 1; i <= k; i++) {
int ff = 0;
for(int j = 0; j <= 25; j++) {
if(use[j] == s[j].r) continue;
while(ch[j].second < ch[j].first.size() && ch[j].first[ch[j].second] <= last) ch[j].second++;
if(ch[j].second == ch[j].first.size()) continue;
int pos = ch[j].first[ch[j].second];
use[j]++;int flag = 0, sum = 0;
for(int g = 0; g <= 25; g++) {
if(num[pos+1][g]+use[g] < s[g].l) {flag = 1;break;}
sum += max(0, s[g].l-use[g]);
}
if(sum > (k-i)) flag = 1;
sum = 0;
for(int g = 0; g <= 25; g++) {
sum += min(s[g].r-use[g], num[pos+1][g]);
}
if(sum < (k-i)) flag = 1;
if(flag) use[j]--;
else {
last = pos;
res[i] = j+'a';
ff = 1;
break;
}
}
if(!ff) {printf("-1\n"); return;}
}
for(int i = 1; i <= k; i++) putchar(res[i]);
puts("");
return;
}
int main() {
//g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
//freopen("out.txt","w",stdout);
#endif
while(scanf("%s", ss+1) == 1) {
solve();
}
return 0;
}
第二场
占坑,待补
02 Beauty Of Unimodal Sequence
09 I Love Palindrome String
听说是回文树裸题。。就去学了一下回文树。。还真是裸题。。
在回文树维护的本质不同的回文串中统计l-mid也是回文串的个数,其实就是在找回文串是否前一半和后一半是否相同。
看图就应该很好懂。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
typedef long long ll;
const ll mod = 1e9+7;
const int p = 1333;
int Case = 1;
int ret[maxn];
unsigned long long P[maxn], _hash[maxn];
unsigned long long gethash(int l, int r) {
return _hash[r]-_hash[l-1]*P[(r-l+1)];
}
bool check(int l, int r) {
int mid = (l+r)/2;
if((r-l+1)&1) return gethash(l, mid) == gethash(mid, r);
else return gethash(l, mid) == gethash(mid+1, r);
}
struct pam{
int S[maxn];
int len[maxn], fail[maxn], last, n;
int ch[maxn][30], cnt[maxn], tot, id[maxn];
void init() {
tot = 0;
newnode(0);newnode(-1);last = 0;
last = 0;n = 0;S[n] = -1;fail[0] = 1;
}
int newnode(int x) {
len[tot] = x;
cnt[tot] = fail[tot] = 0;
for(int i = 0; i < 26; i++) ch[tot][i] = 0;
return tot++;
}
int getfail(int x) {
while(S[n] != S[n-len[x]-1]) x = fail[x];
return x;
}
void insert(char *s) {
int l = strlen(s);
for(int i = 0; i < l; i++) {
int c = s[i]-'a';
S[++n] = c;
int cur = getfail(last);
if(!ch[cur][c]) {
int now = newnode(len[cur]+2);
fail[now] = ch[getfail(fail[cur])][c];
ch[cur][c] = now;
}
last = ch[cur][c];cnt[last]++;id[last] = n;
}
}
void count() {
for(int i = tot-1; i >= 0; i--) cnt[fail[i]] += cnt[i];
}
void cal() {
for(int i = 0; i < tot; i++) {
if(len[i] > 0) {
ret[len[i]] += check(id[i]-len[i], id[i]-1)*cnt[i];
}
}
}
}pam;
int n, m;
char ss[maxn];
void solve() {
memset(ret, 0, sizeof(ret));
pam.init();
pam.insert(ss);n = strlen(ss);
_hash[0] = ss[0];
for(int i = 1; i < n; i++) {
_hash[i] = _hash[i-1]*p+ss[i];
}
pam.count();pam.cal();
for(int i = 1; i <= n; i++) {
printf("%d%c", ret[i], i==n?'\n':' ');
}
return;
}
int main() {
//g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
//freopen("out.txt","w",stdout);
#endif
P[0] = 1;
for(int i = 1; i < maxn; i++) {
P[i] = P[i-1]*p;
}
while(scanf("%s", ss) == 1) {
solve();
}
return 0;
}
12 Longest Subarray
听说可以for()for()剪枝过。。。
正解思路:枚举右端点,当右端点移动。设位置为i,元素为x,那么元素x对1-n这个区间的影响分为两个区间:x数量为0的区间,x数量大于等于k的区间。设距离i最近的x位置为p1, 距离i的第k个的x位置为pk,那么就是【p1+1,i】,【1, pk】。
线段树初始化每个叶子节点的权值为C,当右端点移动,把[p1+1, i]区间减一,然后把第k个+1到第k+1的区间加一,询问答案就是左边第一个等于C的位置。有点巧妙的套路。。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m, C;
int mx[maxn<<2], lazy[maxn<<2], pos[maxn<<2];
void pushup(int rt) {
mx[rt] = max(mx[rt<<1], mx[rt<<1|1]);
pos[rt] = mx[rt] == mx[rt<<1] ? pos[rt<<1]:pos[rt<<1|1];
}
void build(int rt, int l, int r) {
mx[rt] = lazy[rt] = pos[rt] = 0;
if(l == r) {
mx[rt] = C;
pos[rt] = l;
return;
}
int mid = (l+r)/2;
build(rt<<1, l, mid);
build(rt<<1|1, mid+1, r);
pushup(rt);
}
void pushdown(int rt) {
if(lazy[rt]) {
int &x = lazy[rt];
lazy[rt<<1] += x;lazy[rt<<1|1] += x;
mx[rt<<1] += x;mx[rt<<1|1] += x;
x = 0;
}
}
void update(int rt, int L, int R, int l, int r, int c) {
if(l > r)return;
if(l == L && r == R) {
mx[rt] += c;
lazy[rt] += c;
return;
}
pushdown(rt);
int mid = (R+L)/2;
if(r <= mid) update(rt<<1, L, mid, l, r, c);
else if(l > mid) update(rt<<1|1, mid+1, R, l, r, c);
else update(rt<<1, L, mid, l, mid, c), update(rt<<1|1, mid+1, R, mid+1, r, c);
pushup(rt);
}
int query(int rt, int L, int R, int l, int r) {
if(l > r || mx[rt] != C) return 0;
if(l == L && R == r) return pos[rt];
int mid = (L+R)/2;
pushdown(rt);
if(r <= mid) return query(rt<<1, L, mid, l, r);
else if(l > mid) return query(rt<<1|1, mid+1, R, l, r);
else {
int t;
return (t = query(rt<<1, L, mid, l, mid))?t:query(rt<<1|1, mid+1, R, mid+1, r);
}
}
vector<int>ve[maxn];
int k;
void solve() {
int res = 0;
build(1, 1, n);
for(int i = 1; i <= n; i++) ve[i].clear(), ve[i].push_back(0);
for(int i = 1; i <= n; i++) {
int x;scanf("%d", &x);
if(ve[x].back()+1 <= i) update(1, 1, n, ve[x].back()+1, i, -1);
ve[x].push_back(i);
int p = ve[x].size()-k-1;
if(p >= 0) update(1, 1, n, ve[x][p]+1, ve[x][p+1], 1);
int q = query(1, 1, n, 1, i);
if(q) res = max(res, i-q+1);
}
printf("%d\n", res);
return;
}
int main() {
//g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
//freopen("out.txt","w",stdout);
#endif
while(scanf("%d%d%d", &n, &C, &k) == 3) {
solve();
}
return 0;
}
第三场
04Distribution of books
考虑二分内DP,dp[i]表示前i个数最多能分成多少块。状态转移就是
把前缀和的dp值丢线段树里,维护一下区间最值就行了。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m;
int mx[maxn<<4];
void pushup(int rt) {
mx[rt] = max(mx[rt<<1], mx[rt<<1|1]);
}
void build(int rt, int l, int r) {
mx[rt] = -2000000000;
if(l == r) return;
int mid = (l+r)/2;
build(rt<<1, l, mid);build(rt<<1|1, mid+1, r);
}
void update(int rt, int l, int r, int pos, int val) {
if(l > r) return;
if(l == r) {
mx[rt] = val;return;
}
int mid = (l+r)/2;
if(pos <= mid) update(rt<<1, l, mid, pos, val);
else update(rt<<1|1, mid+1, r, pos, val);
pushup(rt);
}
int query(int rt, int L, int R, int l, int r) {
if(l > r) return 0;
if(l == L && R == r) {
return mx[rt];
}
int mid = (L+R)/2;
if(r <= mid) return query(rt<<1, L, mid, l, r);
else if(l > mid) return query(rt<<1|1, mid+1, R, l, r);
else return max(query(rt<<1, L, mid, l, mid), query(rt<<1|1, mid+1, R, mid+1, r));
}
int cnt[maxn], a[maxn];
ll pre[maxn];
int pos[maxn];
vector<ll>ve;
int getid(ll x) {
return lower_bound(ve.begin(), ve.end(), x) - ve.begin()+1;
}
bool cal(ll x) {
build(1, 1, n+1);update(1, 1, n+1, pos[0], 0);
for(int i = 1; i <= n; i++) {
int p = getid(pre[i]-x);
update(1, 1, n+1, pos[i], query(1, 1, n+1, p, n+1)+1);
}
return mx[1] >= m;
}
void solve() {
ve.clear();memset(cnt, 0, sizeof(cnt));
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
ve.push_back(0);
for(int i = 1; i <= n; i++) pre[i] = pre[i-1] + a[i], ve.push_back(pre[i]);
sort(ve.begin(), ve.end());
ve.erase(unique(ve.begin(), ve.end()), ve.end());
for(int i = 0; i <= n; i++) {
int x = getid(pre[i]);
pos[i] = x + cnt[x];cnt[x]++;
}
ll l = -2e14, r = 2e14;
while(r-l > 1ll) {
ll mid = (l+r)/2;
if(cal(mid)) r = mid;
else l = mid;
}
if(cal(r)) l++;
printf("%lld\n", l);
return;
}
int main() {
//g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d", &Case);
while(Case--) {
solve();
}
return 0;
}
第四场
08K-th Closest Distance
哭了。。赛场上是暴力的用主席树把p的前k个和后k个放进vector里面然后sort,T了一下午。。。
被这个k给骗了。。
可以二分答案,对于mid,查询[p-mid, p+mid]区间中的数是否大于等于k,这玩意显然具有单调性。。。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6+5;
const int mod = 1e9+9;
int Case = 1;
int n, m, root[maxn], tot;
int ll[maxn<<5], rr[maxn<<5], sum[maxn<<5];
void pushup(int rt) {
sum[rt] = sum[ll[rt]] + sum[rr[rt]];
}
void build(int &rt, int l, int r) {
rt = ++tot;
if(l == r) {sum[rt] = 0;return;}
int mid = (l + r)/2;
build(ll[rt], l, mid);
build(rr[rt], mid+1, r);
pushup(rt);
}
void insert(int &rt, int pr, int pos, int l, int r) {
rt = ++tot;
ll[rt] = ll[pr];rr[rt] = rr[pr];
sum[rt] = sum[pr];
if(l == r) {
sum[rt]++;
return;
}
int mid = (l + r)/2;
if(pos <= mid) insert(ll[rt], ll[pr], pos, l, mid);
else insert(rr[rt], rr[pr], pos, mid+1, r);
pushup(rt);
}
int query(int lc, int rc, int l, int r, int L, int R) {
if(L > R) return 0;
if(l == L && R == r) return sum[rc]-sum[lc];
int mid = (l+r)/2;
if(R <= mid) return query(ll[lc], ll[rc], l, mid, L, R);
else if(L > mid) return query(rr[lc], rr[rc], mid+1, r, L, R);
else return query(ll[lc], ll[rc], l, mid, L, mid) + query(rr[lc], rr[rc], mid+1, r, mid+1, R);
}
int k;
int cc[maxn];
vector<int>ve;
int getid(int x) {
return lower_bound(ve.begin(), ve.end(), x)-ve.begin()+1;
}
int cal(int L, int R, int p) {
int l = -1, r = 2e6;
while(r-l>1) {
int mid = (r+l)/2;
int lc = getid(p-mid), num;
int rc = upper_bound(ve.begin(), ve.end(), p+mid)-ve.begin();
//assert(lc <= rc);
num = query(root[L-1], root[R], 1, n, lc, rc);
if(num >= k) r = mid;
else l = mid;
}
return r;
}
void solve() {
tot = 0;ve.clear();
scanf("%d%d", &n, &m);
build(root[0], 1, n);
for(int i = 1; i <= n; i++) {
scanf("%d", &cc[i]);
ve.push_back(cc[i]);
}
sort(ve.begin(), ve.end());
for(int i = 1; i <= n; i++) {
insert(root[i], root[i-1], getid(cc[i]), 1, n);
}
int last = 0;
for(int i = 1; i <= m; i++) {
int l, r, P;
scanf("%d%d%d%d", &l, &r, &P, &k);
l ^= last;r ^= last;k ^= last;P ^= last;
if(l > r) swap(l, r);
printf("%d\n", last = cal(l, r, P));
}
//cout<<"!!!"<<endl;
return;
}
int main() {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
scanf("%d", &Case);
//scanf("%d", &Case);
while(Case--) {
solve();
}
return 0;
}
第五场
04equation
把每对a,b看成以-b/a对称的直线,题目转化为这些直线叠加之后是否穿过C。细节很多,具体看代码。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define ft first
#define sd second
#define all(c) ((c).begin()), ((c).end())
#define mp(a, b) make_pair(a, b)
#define pb(x) push_back(x)
template<typename T>T gcd(T a, T b) {return b==0?a:gcd(b, a%b);}
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) {printf("debug ");printf(fmt,##__VA_ARGS__);puts("");}
#else
#define debug(fmt, ...)
#endif
const int maxn = 1e5+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, C, m;
struct dk{
ll a, b;
dk(ll _a = 0, ll _b = 0):a(_a), b(_b) {
if(a < 0) {a = -a;b = -b;}
}
void gao() {ll d = gcd(a, abs(b)); a /= d; b /= d;}
bool operator<(const dk s)const{
return b*s.a < s.b*a;
}
bool operator<=(const dk s)const{
return b*s.a <= s.b*a;
}
bool operator==(const dk s)const{
return a*s.b == s.a*b;
}
}cc[maxn];
vector<dk>res;
void solve() {
cin>>n>>C;res.clear();
ll sa = 0, sb = 0;
for(int i = 1; i <= n; i++) {
cin>>cc[i].a>>cc[i].b;
cc[i].b = -cc[i].b;
if(cc[i].a < 0) {
cc[i].a = -cc[i].a;
cc[i].b = -cc[i].b;
}
sa += cc[i].a;sb += cc[i].b;
}
sort(cc+1, cc+1+n);
{dk t(sa, C+sb);if(cc[n] <= t) res.pb(t);}
for(int i = n; i >= 1; i--) {
sa -= 2*cc[i].a;sb -= 2*cc[i].b;
if(!sa) {
if(C+sb == 0) {
if(cc[i-1] < cc[i]) {
printf("-1\n");
return;
}
}
continue;
}
dk t(sa, C+sb);
if(i != 1) {
if(cc[i-1] <= t && t <= cc[i]) {
res.pb(t);
}
}
else if(t <= cc[1]){res.pb(t);}
}
for(int i = 0; i < res.size(); i++) res[i].gao();
sort(res.begin(), res.end());
res.erase(unique(res.begin(), res.end()), res.end());
printf("%d", (int)res.size());
for(int i = 0; i < res.size(); i++) printf(" %lld/%lld", res[i].b, res[i].a);
puts("");
return;
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);std::cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
//freopen("out1.txt","w",stdout);
#endif
//scanf("%d", &Case);
cin>>Case;
while(Case--) {
solve();
}
return 0;
}
05permutation 1
菜到不能呼吸,,没想到还能dfs。。。太蠢了。。。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define ft first
#define sd second
#define all(c) ((c).begin()), ((c).end())
#define mp(a, b) make_pair(a, b)
#define pb(x) push_back(x)
const int maxn = 105+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m;
int v[maxn], a[maxn];
bool dfs(int p, int l, int r) {
if(p == n+1) {
m--;
if(!m) {
for(int i = 1; i <= n; i++) printf("%d%c", a[i]-l+1, i==n?'\n':' ');
return true;
}
return false;
}
for(int i = r-n+1; i <= l+n-1; i++) {
if(v[i]) continue;
v[i] = 1;
a[p] = i;
if(dfs(p+1, min(l, i), max(r, i))){
v[i] = 0;
return 1;
}
v[i] = 0;
}
return 0;
}
void solve() {
cin>>n>>m;
a[1] = n;
v[n] = 1;
dfs(2, n, n);
v[n] = 0;
return;
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);std::cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
//freopen("out.txt","w",stdout);
#endif
//scanf("%d", &Case);
cin>>Case;
while(Case--) {
solve();
}
return 0;
}