码迷,mamicode.com
首页 > 其他好文 > 详细

Educational Codeforces Round 101 (Rated for Div. 2) 题解

时间:2021-02-16 12:18:20      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:最优   时间复杂度   没有   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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!