Loading

Codeforces 16xx 合集

CF1616

A. Integer Diversity

直接用个map贪心,如果有相同的就反向即可。

B. Mirror in the String

这道题洛谷的翻译锅了,所以建议去看原题。

考虑这样一个字符串 baacc,那么答案显然应该是 baaaab。那么我们可以猜答案就是一段前缀不升的字符串。那证明是显然的,但是还漏考虑了一种情况。就是前两个字符相同的时候,直接取第一个字符即可。复杂度线性。

C. Representative Edges

\(a_1+a_2=a_1+a_2\)\(\frac{3}{2}(a_1+a_3)=a_1+a_2+a_3\),两式作差,可得 \(a_2-a_1=a_3-a_2\)。也就是说这是个等差序列(其实这不就是等差数列的求和公式吗)。那么直接枚举两个位置不动,剩下的位置的值也就可以算出来了。

D. Keep the Average High

直接暴力dp,用平衡树优化,复杂度线性对数。

E. Lexicographically Small Enough

直接找到第一个比这个位置小的,然后移过来,或者找到第一个和这个位置相同的之后变成子问题。

这个东西也可以用平衡树维护,复杂度也是线性对数。

F. Tricolor Triangles

这个好像没什么构造方法,直接列方程,由于三元环是 \(O(m^{1.5})\) 的,所以直接暴力高斯消元即可。然后由于这个矩阵贼稀疏,所以直接跑好像一点问题都没有。复杂度 \(O(m^{4.5})\)。其实复杂度没有这么高,只有 \(m^{4}\)

可以bitset优化,但是vp的时候懒得仔细想了。

CF1628

A. Meximum Array

显然整个序列的mex最大,我们想要取到这个,找到第一个这个值的位置,显然mex递增。然后变成子任务。但其实不需要每次都算一遍全部的mex,因为每一段一定是一个后缀所以直接预处理好即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
template<typename T>
void read(T &x){
	T sgn=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			sgn=-1;
	for(x=0;isdigit(ch);ch=getchar())
		x=x*10+ch-'0';
	x*=sgn;
}
int n;
int a[maxn],mex[maxn],cnt[maxn];
vector<int>ans;
void MAIN(){
	read(n);
	for(int i=0;i<=n;i++)
		cnt[i]=0;
	for(int i=1;i<=n;i++)
		read(a[i]);
	mex[n+1]=0;
	for(int i=n;i;i--){
		cnt[a[i]]++;
		mex[i]=mex[i+1];
		while(cnt[mex[i]])
			mex[i]++;
	}
	for(int i=0;i<=n;i++)
		cnt[i]=0;
	ans.clear();
	for(int i=1,j;i<=n;i=j){
		int now=0;
		for(j=i;j<=n;j++){
			cnt[a[j]]++;
			while(cnt[now])
				now++;
			if(now==mex[i]){
				ans.push_back(now);
				j++;
				break;
			}
		}
		for(int k=i;k<j;k++)
			cnt[a[k]]--;
	}
	printf("%d\n",(int)ans.size());
	for(int i:ans)printf("%d ",i);
	puts("");
}
int main(){
	int T;
	read(T);
	while(T--)
		MAIN();
	return 0;
}

B. Peculiar Movie Preferences

一共只有几种方法:

  1. 本身就是一个回文串

  2. 2+2,3+3

  3. 2+3,3+2

手玩一下发现不会再有没被包含其他情况。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
template<typename T>
void read(T &x){
	T sgn=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			sgn=-1;
	for(x=0;isdigit(ch);ch=getchar())
		x=x*10+ch-'0';
	x*=sgn;
}
int n,len[maxn];
string s[maxn],t[maxn];
map<string,int>mp;
void MAIN(){
	bool ans=false;
	read(n);
	for(int i=1;i<=n;i++){
		cin>>s[i];
		len[i]=s[i].length();
		t[i]=s[i];
		reverse(t[i].begin(),t[i].end());
		if(s[i]==t[i])
			ans=true;
	}
	mp.clear();
	for(int i=1;i<=n;i++){
		string now;
		now="";
		for(int j=0;j<2;j++)now+=t[i][j];
		if(mp.find(now)!=mp.end())
			ans=true;
		if(mp.find(t[i])!=mp.end())
			ans=true;
		mp[s[i]]=1;
	}
	mp.clear();
	for(int i=n;i;i--){
		string now;
		now="";
		for(int j=0;j<2;j++)now+=s[i][j];
		if(mp.find(now)!=mp.end())
			ans=true;
		if(mp.find(s[i])!=mp.end())
			ans=true;
		mp[t[i]]=1;
	}
	
	puts(ans?"YES":"NO");
}
int main(){
	int T;
	read(T);
	while(T--)
		MAIN();
	return 0;
}

C. Grid Xor

发现直接暴力的构造就是对的。打出来之后也很可以看出规律。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
template<typename T>
void read(T &x){
	T sgn=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			sgn=-1;
	for(x=0;isdigit(ch);ch=getchar())
		x=x*10+ch-'0';
	x*=sgn;
}
int n,a[1005][1005];
bool vis[1005][1005];
void MAIN(){
	read(n);
	for(int i=0;i<=n+1;i++)
		for(int j=0;j<=n+1;j++)
			vis[i][j]=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			read(a[i][j]);
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(!vis[i-1][j]&&!vis[i][j-1]&&!vis[i+1][j]&&!vis[i][j+1]){
				ans^=a[i][j];
				vis[i-1][j]=vis[i][j-1]=vis[i+1][j]=vis[i][j+1]=1;
			}
		}
	}
	printf("%d\n",ans);
}
int main(){
	int T;
	read(T);
	while(T--)
		MAIN();
	return 0;
}

D. Game on Sum (Hard Version)

这个 \(k\) 是来吓唬你的,显然最后给答案乘上 \(k\) 即可。然后考虑只能在 \(0,1\) 之间取数。设 \(f_{i,j}\) 代表还剩 \(i\) 次操作需要 \(j\) 次加操作。考虑转移,显然他会从 \(f_{i-1,j-1}+d,f_{i-1,j}-d\) 转移过来时有一种方法变成两者的平均数。于是他构成一个数表:

0
0 1
0 1/2 2
0 1/4 5/4 3
...

考虑右边的数对答案的贡献,容易发现向下一层就会/2,这个先不管。贡献次数即为路径条数,每次可以向下或右下走一步(除了第一步只能向下),所以共有 \(\binom{n-i-1}{m-i}\) 种走法。那么答案即为 \(\sum_{i=1}^{m}\binom{n-i-1}{m-i}2^{n-i}\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5,mod=1e9+7;
int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
int dec(int a,int b){return a<b?a-b+mod:a-b;}
int mul(int a,int b){return 1ll*a*b%mod;}
int ksm(int a,int b=mod-2){int ret=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
template<typename T>
void read(T &x){
	T sgn=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			sgn=-1;
	for(x=0;isdigit(ch);ch=getchar())
		x=x*10+ch-'0';
	x*=sgn;
}
int n,m,k,fac[maxn],ifac[maxn];
int C(int a,int b){
	if(a<0||b<0||a<b)return 0;
	return mul(fac[a],mul(ifac[b],ifac[a-b]));
}
void MAIN(){
	read(n);read(m);read(k);
	fac[0]=ifac[0]=1;
	for(int i=1;i<=n;i++)fac[i]=mul(fac[i-1],i);
	ifac[n]=ksm(fac[n]);
	for(int i=n-1;i;i--)ifac[i]=mul(ifac[i+1],i+1); 
	int ans=0;
	if(m==n)ans=n;
	else{
		for(int i=1;i<=m;i++){
			ans=add(ans,mul(i,mul(C(n-i-1,m-i),ksm(ksm(2,n-i)))));
		}
	}
	printf("%d\n",mul(ans,k));
}
int main(){
	int T;
	read(T);
	while(T--)
		MAIN();
	return 0;
}

E. Groceries in Meteor Town

E 才是最傻逼。

显然要求瓶颈路,那么建出kruscal重构树,然后答案即为 x 和每个白色点lca的lca的值。显然即为所有白色点lca和x的lca的值。也就是维护所有白色点的lca即可。lca显然可以合并,于是线段树直接做。

#include<bits/stdc++.h>
using namespace std;
const int maxn=6e5+5;
template<typename T>
void read(T &x){
	T sgn=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			sgn=-1;
	for(x=0;isdigit(ch);ch=getchar())
		x=x*10+ch-'0';
	x*=sgn;
}
struct edge{
	int u,v,w;
}e[maxn];
bool operator<(edge a,edge b){
	return a.w<b.w;
}
int n,m,q,fa[maxn],cnt,id[maxn],val[maxn];
vector<int>vec[maxn];
int f[maxn][21],tr[maxn<<2],lca[maxn<<2],tag[maxn<<2],dep[maxn];
int Find(int x){
	return fa[x]==x?x:fa[x]=Find(fa[x]);
}
int LCA(int u,int v){
	if(dep[u]<dep[v])swap(u,v);
	int d=dep[u]-dep[v];
	for(int i=0;i<=20;i++)
		if(d&(1<<i))
			u=f[u][i];
	if(u==v)
		return u;
	for(int i=20;~i;i--)
		if(f[u][i]!=f[v][i]){
			u=f[u][i];
			v=f[v][i];
		}
	return f[u][0];
}
void dfs(int u){
	for(int i=1;i<=20;i++)f[u][i]=f[f[u][i-1]][i-1];
	for(int v:vec[u]){
		f[v][0]=u;
		dep[v]=dep[u]+1;
		dfs(v);
	}
}
void build(int p,int l,int r){
	tr[p]=tag[p]=-1;
	if(l==r)
		return lca[p]=l,void();
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	lca[p]=LCA(lca[p<<1],lca[p<<1|1]);
}
void pusht(int p,int v){
	if(v)tr[p]=-1;
	else tr[p]=lca[p];
	tag[p]=v;
}
void push_up(int p){
	if(tr[p<<1]==-1)tr[p]=tr[p<<1|1];
	else if(tr[p<<1|1]==-1)tr[p]=tr[p<<1];
	else tr[p]=LCA(tr[p<<1],tr[p<<1|1]);
}
void push_down(int p){
	if(tag[p]!=-1){
		pusht(p<<1,tag[p]);
		pusht(p<<1|1,tag[p]);
		tag[p]=-1;
	}
}
void modify(int p,int l,int r,int x,int y,int v){
	if(l>y||r<x)
		return;
	if(x<=l&&r<=y)
		return pusht(p,v),void();
	int mid=(l+r)>>1;
	push_down(p);
	modify(p<<1,l,mid,x,y,v);
	modify(p<<1|1,mid+1,r,x,y,v);
	push_up(p);
}
void MAIN(){
	read(n);read(q);
	m=n-1;
	for(int i=1;i<n;i++){
		read(e[i].u);read(e[i].v);read(e[i].w); 
	}
	sort(e+1,e+1+m);
	cnt=n;
	for(int i=1;i<=n;i++)id[i]=fa[i]=i;
	for(int i=1;i<=m;i++){
		int u=e[i].u,v=e[i].v;
		int fu=Find(u),fv=Find(v);
		if(fu==fv)continue;
		val[++cnt]=e[i].w;
		vec[cnt].push_back(id[fu]);
		vec[cnt].push_back(id[fv]);
		id[fu]=id[fv]=cnt;
		fa[fu]=fv;
	}
	dfs(cnt);
	build(1,1,n);
	while(q--){
		int op,l,r,x;
		read(op);
		if(op==1){
			read(l);read(r);
			modify(1,1,n,l,r,0);
		}else if(op==2){
			read(l);read(r);
			modify(1,1,n,l,r,1);
		}else{
			read(x);
			if(tr[1]==-1||tr[1]==x)puts("-1");
			else printf("%d\n",val[LCA(tr[1],x)]);
		}
	}
}
int main(){
	int T=1;
	while(T--)
		MAIN();
	return 0;
}

A. GCD vs LCM

构造 \(1,n-3,1,1\) 即可。

B. Array Cloning Technique

贪心策略:复制一遍移到原来序列,再重复这个过程。

C. Tree Infection

现在所有非叶节点的儿子以及根处放一个,然后根据儿子大小从大到小排序贪心。剩下的部分模拟即可。

D. GCD Guess

考虑从低到高逐位确定,最多30位正好用完。\(\gcd(x+a,x+b)=\gcd(x+a,a-b)\),我们现在想知道第 \(i\) 位上是不是 \(1\),前 \(i-1\) 位已经确定为 \(x'\),那么我们只需要知道 \(\gcd(x-x'+2^i,2^{i+1})\) 即可。那么我们构造 \(a=3\times 2^{i}-x',b=2^{i}-x'\) 即可。验证可得这样符合条件。

E. MinimizOR

结论题,答案一定在最小的 \(k+1\) 个数里,所有数 \(<2^{k}\)。考虑归纳证明,假设所有数二进制下 \(<2^1\),也就是只有 \(0,1\),那么结论显然成立。考虑 \(k=x\) 时成立,推到 \(k=x+1\)。如果最高位全为 \(1\) 那么依然成立,否则一定选择两个最高位为 \(0\) 的,那么就是在最小的里面找,有可能最高位只有一个为 \(0\),那么就得多找一个数。

然后用线段树维护,时间复杂度 \(O(n\log n\log V)\)

CF1677

A. Tokitsukaze and Strange Inequality

给定一个排列 \(p\),求有多少个四元组 \((a,b,c,d),a<b<c<d\) 满足 \(p_a<p_c\)\(p_b>p_d\)

\(1\le n\le 5000.\)

暴力枚举 \(a,c\),先枚举 \(c\) 再 枚举 \(a\)。然后扩展 \(a\) 的时候就可以求出 \(p_b>p_d\) 的个数,复杂度 \(O(n^2)\)

B. Tokitsukaze and Meeting

现在 \(n\times m\) 名学生将按以下方式就坐

  • 每一轮,所有学生坐到右侧的座位(最后一列的坐到下一行第一列),空出第一行第一列,当前学生在第一行第一列就坐。学生有属性 \(0/1\)

对于每一时刻,有多少列或行满足,当前列/行存在属性为 \(1\) 的学生?

\(1\le n\times m\le 10^6.\)

一个傻逼做法。

显然行列做法不太一样我们分开来求。

列的话每次相当于一个循环位移,那么答案不会变小。直接维护即可。

行的话相当于 \(i-m\) 的答案加上第一行,那么只需要看一看 \([i-m+1,i]\) 这一段有没有 \(1\) 即可。

C. Tokitsukaze and Two Colorful Tapes

有两个颜色排列 \(a\)\(b\),两个排列中相同颜色的位置可以填充 \(1\sim n\) 中的同一个数字且不可重复如果使得最后数字序列为:\(a_{i},b_{i}\)。要求最大化 \(\sum_{i=1}^{n}|numa_{i}-numb_{i}|\)

\(1\le n\le 10^5.\)

将相同颜色位置连起来,假设形成的每个环的长度为 \(L_i\),那么 \(m=\sum\lfloor\frac{L_i}{2}\rfloor\)。贪心地看,应该一大一笑论者放,那么平均一下,答案是 \(2m(n-m)\)

D. Tokitsukaze and Permutations

有一个长度为 \(n\) 的数组 \(p\),将执行 \(k\) 次操作操作过程:对于 \(1\sim n\) 中,当\(p_{i}>p_{i+1}\),则交换 \(p_{i},p_{i+1}\) 经过 \(k\) 次操作之后,得到了一个新数组 \(a\)
定义数组 \(v\) 表示在 \(1\sim i\) 中比 \(p_{i}\) 大的个数。现在给定 \(v\),但是有可能其中的值为 \(-1\),这表示它的值并不确定。求有多少种 \(p\) 满足在 \(k\) 次操作后得到的 \(v\) 和给定确定值一致。

\(1\le n\le 10^6.\)

容易发现一次操作即为整体左移一位,然后 \(v_i=min(v_i-1,0)\),那么 \(p\)\(v\) 就是双射。先判断 \(v\) 是否合法,然后将 \(v\) 还原。接下来对 \(v\) 计数,如果遇到 \(-1\),就是 \(i\) 种,否则遇到 \(0\) 就是 \(k+1\) 种,否则就是原先的值。

E. Tokitsukaze and Beautiful Subsegments

给定一个长度为 \(n\) 的排列 \(p\)

定义一个区间 \([l,r]\) 是美丽的当且仅当该区间中存在两个 \(i,j\) 满足 \(l\leq i<j\leq r\),且有 \(p_ip_j=\max\limits_{k=l}^rp_k\)

现有 \(q\) 组询问,每组询问给定区间 \([l_i,r_i]\),请你求出其有多少个子区间是美丽的。

\(1\leq n\leq2\times10^5,1\leq q\leq 10^6.\)

这个信息不是分治信息所以其实不好合并的,考虑找出所有 \((i,j,k)\),满足 \(a_ia_j=a_k\),其实只有 \(O(n\ln n)\) 组,那对于这个可以写成一个矩形。那么我们就是求矩形并,所以扫描线就好了 \(O(q\log n+n\log^2n)\)

F. Tokitsukaze and Gems

我不知道这种东西出出来的意义是啥。

posted @ 2022-04-09 01:08  Semsue  阅读(108)  评论(0编辑  收藏  举报
Title