标签:最优 时间复杂度 没有 its 高度 逆向 std div mat
A,B就不讲了
C
不妨设\(a_i\)为板离地面的高度,首先我们知道\(a_1=0\)且\(a_n=0\)
很容易我们容易发现一条性质:
\(h_{i-1}+a_{i-1}<h_i+a_i+k\)且\(h_i+a_i<h_{i-1}+a_{i-1}+k\)
化简一下可以得到\(max(0,h_{i-1}+a_{i-1}-k+1)\le h_i+a_i \le min(k-1,h_{i-1}+a_{i-1}+k-1)\)
知道\(a_i\)与\(a_{i-1}\)正相关
之后我们设\(L_i\)为\(a_i\)可取到的下界,\(R_i\)为\(a_i\)可以取到的上届
每次通过像不等式左边和右边的式子递推即可
如果有一个\(i\)满足\(L_i>R_i\),或者\(L_n>0\) 或者\(R_n<0\)都是无解的情况
时间复杂度\(O(n)\)
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<"="<<x<<endl
#define lowbit(x) x&(-x)
#define pii pair<int,int>
#define mk make_pair
#define ll long long
#define lb long double
#define rs p<<1|1
#define ls p<<1
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline ll read()
{
ll p=0,f=1;char c=getchar();
while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
while(c>=‘0‘&&c<=‘9‘){p=(p<<1)+(p<<3)+(c^48),c=getchar();}
return f*p;
}
int h[maxn], a[maxn], L[maxn], R[maxn];
int n, k;
void solve()
{
scanf("%d%d",&n,&k);
for(int i = 1;i <= n; i++) scanf("%d",&h[i]);
a[1] = 0;
for(int i = 2;i <= n; i++)
L[i] = max(0,h[i - 1] - h[i] - k + L[i - 1] + 1);
for(int i = 2;i <= n; i++)
R[i] = min(k - 1,h[i - 1] - h[i] + k + R[i - 1] - 1);
for(int i = 1;i <= n; i++)
if(L[i] > R[i]) { puts("NO"); return; }
if(L[n] > 0 || R[n] < 0) { puts("NO"); return; }
puts("YES");
}
signed main()
{
int t = read();
while(t --) solve();
}
D
构造
我们可以考虑递归的思路
首先我们处理\(\sqrt{n}+1\)到\(n\)的一块
先可以尝试把这一块全部变为\(1\)
即我们先用所有其他元素(除了\(\sqrt{n}+1\))除以最大元素
这样它们都会变为1,之后让\(n\)除两次\(\sqrt{n}+1\),
这样所有元素除了\(\sqrt{n}+1\)都会变为\(1\)
之后\(n=\sqrt{n}+1\)递归下去即可
容易发现,通过不断的取根号,我们操作的总次数是不会超过\(n+3\)的
这样的算法是符合条件的
时间复杂度\(O(n)\)
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<"="<<x<<endl
#define lowbit(x) x&(-x)
#define pii pair<int,int>
#define mk make_pair
#define ll long long
#define lb long double
#define rs p<<1|1
#define ls p<<1
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline ll read()
{
ll p=0,f=1;char c=getchar();
while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
while(c>=‘0‘&&c<=‘9‘){p=(p<<1)+(p<<3)+(c^48),c=getchar();}
return f*p;
}
int n, a[maxn], ans1[maxn], ans2[maxn];
void solve()
{
scanf("%d",&n);
for(int i = 1;i <= n; i++) a[i] = i;
int cnt = 0;
while(233)
{
int pos = (int)sqrt(n) + 1;
for(int i = pos + 1;i < n; i++)
ans1[++ cnt] = i, ans2[cnt] = n;
ans1[++ cnt] = n;ans2[cnt] = pos;
ans1[++ cnt] = n;ans2[cnt] = pos;
n = pos;
if(n <= 2) break;
}
printf("%d\n",cnt);
for(int i = 1;i <= cnt; i++)
printf("%d %d\n",ans1[i],ans2[i]);
}
signed main()
{
int t = read();
while(t --) solve();
}
E
哈希即可
考虑逆向思维,寻找字典序最小的串满足条件
如果存在\(i \in{[1,k]}, a_i =b_i\)只要\(b\)不是\(a\)的取反串即可
即找到最小的一个串,不是\(n-k+1\)的取反串即可
这样我们就可以哈希了
我们可以通过递推,求出这\(n-k+1\)个串的哈希值之后存储到一个桶中
容易知道我们最多枚举\(min(2^{k},n-k+2)\)次,就会找到符合条件的串
枚举到一个串,如果该串的哈希值没有在桶中出现,那么我们就可以直接输出答案了
最好不要用\(map\)会TLE,直接用\(19210817\)即可通过本题
#include<bits/stdc++.h>
//#include<tr1/unordered_map>
using namespace std;
//using namespace tr1;
#define debug(x) cout<<#x<<"="<<x<<endl
#define lowbit(x) x&(-x)
#define pii pair<int,int>
#define mk make_pair
#define ull unsigned long long
#define int long long
#define lb long double
#define rs p<<1|1
#define ls p<<1
const int maxn = 1e6 + 5;
const int mod = 19210817;
const int inf = 0x3f3f3f3f;
int n, k;
char s[maxn], s2[maxn], ans[maxn];
int base[maxn];
bool mp[mod + 5];
void solve()
{
scanf("%lld%lld",&n,&k);
scanf("%s",s + 1);
for(int i = 1;i <= n; i++)
s2[i] = (s[i] == ‘0‘) ? ‘1‘ : ‘0‘;
base[0] = 1;
for(int i = 1;i <= k; i++) base[i] = (base[i - 1] * 2) % mod;
int num = 0;
for(int i = 1;i <= k; i++)
num = (num * 2 + s2[i] - ‘0‘) % mod;
mp[num] = 1;
for(int i = k + 1; i <= n; i++)
{
num = ((num * 2 + s2[i] - ‘0‘) % mod + mod) % mod;
num = ((num - (s2[i - k] - ‘0‘) * base[k]) % mod + mod) % mod;
mp[num] = 1;
}
num = 0;
int lim = 0;
if(k <= 20) lim = base[k];
else lim = n - k + 2;
int flag = 0;
for(int i = 0;i < lim; i++)
{
num = i;
if(!mp[num])
{
puts("YES");
for(int j = k - 1;j >= 28; j--) printf("0");
for(int j = min(k - 1,(int)27);j >= 0; j--)
printf("%d",(num & (1 << j))?1:0);
printf("\n");
flag = 1;
//return;
break;
}
}
if(!flag) puts("NO");
num = 0;
for(int i = 1;i <= k; i++)
num = (num * 2 + s2[i] - ‘0‘) % mod;
mp[num] = 0;
for(int i = k + 1; i <= n; i++)
{
num = ((num * 2 + s2[i] - ‘0‘) % mod + mod) % mod;
num = ((num - (s2[i - k] - ‘0‘) * base[k]) % mod + mod) % mod;
mp[num] = 0;
}
}
signed main()
{
int t ; scanf("%lld",&t);
while(t --) solve();
}
F
首先很容易通过观察发现三个性质:
1.我们每次选择剩余的串中最长的拼是最优的
2.每次拼的过程中选择链的重心拼接是最优的
3.每次选择的链的重心与距离根最近的白点拼接是最优的
关于这三点的性质的证明,就不在这里赘述了(可查看官方题解)
之后拼接的过程我们可以通过动态开点的线段树模拟拼接过程
每次\(k\)与根的距离取\(min\)即可
(要注意动态开点线段树的写法,以及区间第\(k\)大实现的细节)
时间复杂度\(O(nlog(max(a_i)))\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 4e6 + 101;
const int lim = 1e9;
int tree[N], lc[N], rc[N], laz[N];
int a[N], n, k, cnt = 1;
void push_up(int p)
{
tree[p] = tree[lc[p]] + tree[rc[p]];
}
void push_down(int p,int l,int r)
{
int mid = (l + r) / 2;
tree[lc[p]] += (mid - l + 1) * laz[p];
tree[rc[p]] += (r - mid) * laz[p];
laz[lc[p]] += laz[p];
laz[rc[p]] += laz[p];
laz[p] = 0;
}
int flag;
void update(int p,int l,int r,int nl,int nr,int k)
{
if(nl > nr) return;
if(nl <= l && r <= nr)
{
tree[p] += (r - l + 1) * k;laz[p] += k;
if(nl == nr && k == -1 && tree[p] == 0) flag = 1;
return;
}
if(!lc[p]) lc[p] = ++ cnt;
if(!rc[p]) rc[p] = ++ cnt;
int mid = (l + r) / 2;
push_down(p,l,r);
if(nl <= mid) update(lc[p],l,mid,nl,nr,k);
if(nr > mid) update(rc[p],mid + 1,r,nl,nr,k);
push_up(p);
}
int query_kth(int p,int l,int r,int k)
{
if(l == r) return l;
if(!lc[p]) lc[p] = ++ cnt;
if(!rc[p]) rc[p] = ++ cnt;
int mid = (l + r) / 2;
push_down(p,l,r);
if(tree[lc[p]] >= k) query_kth(lc[p],l,mid,k);
else query_kth(rc[p],mid + 1,r,k - tree[lc[p]]);
}
signed main()
{
scanf("%lld%lld",&n,&k);
for(int i = 1;i <= n; i++) scanf("%lld",&a[i]);
sort(a + 1,a + n + 1,greater<int>());
if(a[1] % 2) update(1,1,lim,2,1+(a[1]-1)/2,2);
else { update(1,1,lim,2,1+a[1]/2,1);update(1,1,lim,2,a[1]/2,1); }
int nowcnt = a[1] - 1, ans = lim;
if(nowcnt >= k)
{
ans = query_kth(1,1,lim,k);
}
for(int i = 2;i <= n; i++)
{
flag = 0;
int pos = query_kth(1,1,lim,1);
update(1,1,lim,pos,pos,-1);nowcnt --;
if(a[i] % 2) update(1,1,lim,pos+2,pos+1+(a[i]-1)/2,2),nowcnt += a[i] - 1;
else update(1,1,lim,pos+2,pos+1+a[i]/2,1),update(1,1,lim,pos+2,pos+a[i]/2,1),nowcnt += a[i] - 1;
if(nowcnt >= k)
ans = min(ans,query_kth(1,1,lim,k));
}
if(ans == lim) puts("-1");
else printf("%lld\n",ans);
return 0;
}
Educational Codeforces Round 101 (Rated for Div. 2) 题解
标签:最优 时间复杂度 没有 its 高度 逆向 std div mat
原文地址:https://www.cnblogs.com/zjz2333/p/14399276.html