首页 > 其他分享 >Codeforces Round 869

Codeforces Round 869

时间:2023-05-14 22:11:47浏览次数:44  
标签:869 const fstpos int sum Codeforces while fst Round

\(\texttt{A. Almost Increasing Subsequence}\)

把 \(a_i>a_{i+1}\)的极长下降区间称为一个段,那么显然,一个长度为 \(len\) 的极长下降区间最多选 \(\min(2,len)\)个,并且可以达到这个数,因此记录每个位置属于哪个段,记录个前缀和就好了。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+7;
int n,m,a[N],bel[N],st[N],ed[N]; 
int sum[N];
int main()
{
	cin>>n>>m;
	int o=0;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		int j=i;
		while(j+1<=n&&a[j+1]<=a[j])j++;
		++o;
		st[o]=i;
		ed[o]=j;
		sum[o]=sum[o-1]+min(2,j-i+1);
		for(int k=i;k<=j;k++)bel[k]=o;
		i=j;
	}
	while(m--)
	{
		int l,r;
		scanf("%d %d",&l,&r);
		int res=0;
		if(bel[l]==bel[r])res=min(2,r-l+1);
		else 
		{
			res=sum[bel[r]-1]-sum[bel[l]];
			res+=min(2,ed[bel[l]]-l+1);
			res+=min(2,r-st[bel[r]]+1);
		}
		printf("%d\n",res);
	}
	return 0;
} 

\(\texttt{B. Fish Graph}\)

一个点集可以构成鱼,一个必要条件就是,形成一个环,且有一个点的度数 \(\geq 4\).

事实上这也是充分的。

对于度数\(\geq 4\)的点 \(u\),假如它在环上相邻的点事 \(a,b\),另外还有两条出边连向 \(x,y\)。

如果 \(x,y\) 不在环上,显然就是合法的。

否则,我们可以把环调整成 \(…… \to y\to u\to x\to ……\),这时 \(a,b\) 就不在环上了,就合法了。
判断是不是在某个环里可以用边双,对于 \(u\),可以建出 \(\text{BFS}\) 树,对于属于不同子树的横叉边,就构成了一个环,并且因为 \(\text{BFS}\),所以环是最小的,直接输出就好了。

#include<bits/stdc++.h>
using namespace std;
const int N = 4e5+7;
struct edge
{
	int y,next;
}e[2*N];
int link[N],t=1;
void add(int x,int y)
{
	e[++t].y=y;
	e[t].next=link[x];
	link[x]=t;
}
int dfn[N],low[N],bel[N],num;
bool cut[N];
int n,m,deg[N],cnt=0,siz[N],fst[N],frt[N],vis[N];
void init()
{
	for(int i=1;i<=t;i++)cut[i]=0;
	t=1;cnt=0;
	for(int i=1;i<=n;i++)dfn[i]=low[i]=bel[i]=link[i]=deg[i]=siz[i]=fst[i]=frt[i]=vis[i]=0;
	num=0;
}
void tarjan(int x,int from)
{
	dfn[x]=low[x]=++num;
	for(int i=link[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(!dfn[y])
		{
			tarjan(y,i);
			if(low[y]>dfn[x])cut[i]=cut[i^1]=1;
			low[x]=min(low[x],low[y]);
		}
		else if(i!=(from^1))low[x]=min(low[x],dfn[y]);
	}
}
int E=0;
void mark(int x)
{
	E++;
	if(E>n)exit(0);
	bel[x]=cnt;
	siz[cnt]++;
	for(int i=link[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(cut[i])continue;
		if(bel[y])continue;
		mark(y);
	}
}
queue<int> q;
void getans(int o)
{
	while(!q.empty())q.pop();
	q.push(o);
	int G=0;
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=link[x];i;i=e[i].next)
		{
			int y=e[i].y;
			if(y==o)continue;
			if(!fst[y])
			{	
				if(x==o)frt[y]=y;
				else frt[y]=frt[x];
				fst[y]=x;
				q.push(y);
			}
			else if(x!=o&&frt[x]!=frt[y])
			{
				int p=x,s=3;
				p=x;while(p!=o)s++,p=fst[p];
				p=y;while(p!=o)s++,p=fst[p];
				printf("%d\n",s);
				p=x;while(p!=o)printf("%d %d\n",fst[p],p),vis[p]=1,p=fst[p];
				p=y;while(p!=o)printf("%d %d\n",fst[p],p),vis[p]=1,p=fst[p];
				printf("%d %d\n",x,y);
				int c=0;
				for(i=link[o];i;i=e[i].next)
				{
					int z=e[i].y;
					if(vis[z])continue;
					printf("%d %d\n",o,z);
					c++;
					if(c==2)return;
				}
			}
		}
	}
}
int S;
void solve()
{
	E=0;
	init();
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d %d",&x,&y);
		deg[x]++;deg[y]++;
		add(x,y);
		add(y,x);
	}
	for(int i=1;i<=n;i++)
	if(!dfn[i])tarjan(i,0);
	for(int i=1;i<=n;i++)
	if(!bel[i])
	{
		++cnt;
		mark(i);
	}
	for(int i=1;i<=n;i++)
	if(deg[i]>=4&&siz[bel[i]]>1)
	{
		printf("YES\n");
		getans(i);
		return;
	}
	printf("NO\n");
}
int main()
{
	int T;
	cin>>T;
	S=T;
	while(T--)
	{
		solve();
	}
	return 0;
}

\(\texttt{C. Similar Polynomials}\)

因为 \(B(x)=A(x+s)\)

所以,

\[\sum_{i=0}^db_ix^i=\sum_{i=0}^da_i(x+s)^i=\sum_{i=0}^da_i\sum_{j=0}^i\binom{i}{j}x_js_{i-j}=\sum_{i=0}^d\sum_{j=i}^d\binom{j}{i}a_js_{j-i}x^i \]

对于 \(i=d-1\),\(b_{d-1}=a_{d-1}s+da_d\),这样就可以得到 \(s\) 啦。

\(b_{d-1},a_{d},a_{d-1}\)可以拉格朗日插值求。

#include<bits/stdc++.h>
using namespace std;
const int N = 3e6+7;
const int mod = 1e9+7;
const int inv2=(mod+1)/2;
int ifac[N],pw[N];
void init(int n)
{
	ifac[1]=1;
	for(int i=2;i<=n;i++)
	ifac[i]=1ll*ifac[mod%i]*(mod-mod/i)%mod;
	ifac[0]=1;
	for(int i=1;i<=n;i++)ifac[i]=1ll*ifac[i-1]*ifac[i]%mod;
	pw[0]=1;
	for(int i=1;i<=n;i++)pw[i]=mod-pw[i-1];
} 
int n,A[N],B[N];
int a[2],b[2];
void calc(int *S,int *arr)
{
	for(int i=0;i<=n;i++)
	{
		int W=1ll*S[i]*pw[n-i]%mod*ifac[i]%mod*ifac[n-i]%mod;
		int inv=mod-(1ll*n*(n+1)%mod*inv2-i+mod)%mod;
		arr[0]=(arr[0]+W)%mod;
		arr[1]=(arr[1]+1ll*W*inv%mod)%mod;
	}
} 
int Pow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)res=1ll*res*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return res;
}
int main()
{
	cin>>n;
	init(n);
	for(int i=0;i<=n;i++)scanf("%d",&A[i]);
	for(int i=0;i<=n;i++)scanf("%d",&B[i]);
	calc(A,a);calc(B,b);
	int s=1ll*(b[1]-a[1]+mod)%mod*Pow(1ll*n*a[0]%mod,mod-2)%mod;
	cout<<s;
	return 0;
}

\(\texttt{D. Toy Machine}\)

找规律!

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,k;
	scanf("%d %d",&n,&k);
	int m=n/2;
	if(k<=m)
	{
		for(int i=1;i<k;i++)printf("LDRU");
		printf("L\n");
		return 0;
	}
	k=n-k-1;
	for(int i=1;i<k;i++)printf("RDLU");printf("R");
	for(int i=1;i<=m;i++)printf("RDRU");printf("LU");
	for(int i=1;i<m;i++)printf("LDRU");printf("L");
	return 0;
}

\(\texttt{E. Half-sum}\)

完全不会。
首先,显然 \(|A-B|=max(A-B,B-A)\),因此就是找出两个集合使得差最大,不放设我们要最大化 \(B-A\)
那么显然,\(B\)里面的数越大越好,因此把序列排序,一定一个前缀是 \(A\),剩下的是 \(B\)。

对于一个有序的\(B\),显然越大的数被合并次数越少越好,因此从小到大合并,A就反过来。
因此有一个 \(O(n^2)\) 的做法就是枚举分界点 \(i\),分成 \([1,i-1],[i,n]\),然后乘上 \(2^n\),比大小。
设 \(b_{i}=a_{i}-a_{i-1}\),可以得出答案从 \(i\to j\) 的变化量 \(D_{i\to j}=-\frac{b_i}{2^{i-1}}+\sum_{k=i+1}^{j-1}b_k(\frac{1}{2_{n-k+1}}-\frac{1}{2^{k-1}})+\frac{b_j}{2^{n-j+1}}\),对于 \(j\leq \frac{n}{2}\),中间部分显然小于0,因此若\(\frac{b_i}{2^{i-1}}\geq \frac{b_j}{2^{n-j+1}}\),就会\(\leq 0\),注意到 \(b_i\) 的影响最多30,因此我们检查一下前 \(30\) 个\(b_i\neq 0\) 的位置和后 30 个就好了。

// LUOGU_RID: 110350855
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+100;
const int mod = 1e9+7;
int Pow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)res=1ll*res*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return res;
}
int a[N],now[N],ans[N];
int n;
void calc(int x)
{
	for(int i=0;i<=n+51;i++)now[i]=0;
	for(int i=1;i<=x;i++)
	{
		int C;
		if(i==x)C=i-1;
		else C=i;
		for(int j=0;j<30;j++)
		now[-C+n+j]-=((a[i]>>j)&1);
	}
	for(int i=x+1;i<=n;i++)
	{
		int C;
		if(i==x+1)C=n-i;
		else C=n-i+1;
		for(int j=0;j<30;j++)
		now[-C+n+j]+=((a[i]>>j)&1);
	}
	for(int i=0;i<=n+50;i++)
	{
		while(now[i]< 0)now[i+1]--,now[i]+=2;
		while(now[i]>=2)now[i+1]++,now[i]-=2;
	}
	if(now[n+51]<0)return;
	for(int i=n+50;i>=0;i--)
	if(now[i]!=ans[i])
	{
		if(now[i]>ans[i])
		{
			for(int j=0;j<=n+50;j++)
			ans[j]=now[j];
		}
		return;
	}
}
void solve()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=0;i<=n+50;i++)ans[i]=0;
	sort(a+1,a+n+1);
	for(int i=2,C=30;i<=n&&C;i++)if(a[i]!=a[i-1])calc(i-1),C--;
	for(int i=n,C=30;i>=2&&C;i--)if(a[i]!=a[i-1])calc(i-1),C--;
	int res=0;
	for(int i=0;i<=n+50;i++)
	if(ans[i])res=(res+Pow(2,i-n+mod-1))%mod;
	printf("%d\n",res);
}

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		solve();
	} 
	return 0; 
}

\(\texttt{F. Entangled Substrings}\)

第一个自己做出来的 \(3500\),虽然比较水。
建出 \(\text{SAM}\),考虑两个结点\(x,y\)什么时候会产生贡献。
那么,首先,他们的 \(endpos-fstpos\) 序列应该是相同的。
其次,从 \(y\) 的出现位置到 \(x\) 出现位置之间的所有位置都应该和 \(y\) 等价,也就是说。
\(fstpos_x\in [fstpos_y-len_y+1,fstpos_y-len_{fa_y}]\)
用字符串哈希维护 \(endpos-fstpos\),对于每一个相同的结点计划,按照 \(fstpos\) 排序,然后对于每个 \(y\),符合条件的都是一个区间,记录前缀和就好了。

// LUOGU_RID: 110085760
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+7;
int tr[N][26],len[N],fa[N],tot=1,last=1,fstpos[N];
typedef long long LL;
typedef unsigned long long ULL;
void copy(int x,int y)
{
	fa[x]=fa[y];
	len[x]=len[y];
	fstpos[x]=fstpos[y];
	for(int c=0;c<26;c++)
	tr[x][c]=tr[y][c];
}
ULL P[N],H[N];
const ULL Base = 13331;
void Extend(int x,int c)
{
	int p=last,np=last=++tot;
	len[np]=len[p]+1;
	fstpos[np]=x;
	H[np]=1;
	while(p&&!tr[p][c])
	{
		tr[p][c]=np;
		p=fa[p];
	}
	if(!p)fa[np]=1;
	else 
	{
		int q=tr[p][c];
		if(len[q]==len[p]+1)fa[np]=q;
		else 
		{
			int nq=++tot;
			copy(nq,q);
			len[nq]=len[p]+1;
			fa[np]=fa[q]=nq;
			while(p&&tr[p][c]==q)
			{
				tr[p][c]=nq;
				p=fa[p];
			}
		}
	}
} 
char s[N];
int n;
struct edge
{
	int y,next;
}e[2*N];
int link[N],t=0;
void add(int x,int y)
{
	e[++t].y=y;
	e[t].next=link[x];
	link[x]=t;
}
void dfs(int x)
{
	for(int i=link[x];i;i=e[i].next)
	{
		int y=e[i].y;
		dfs(y);
		if(x>1)H[x]=H[x]+H[y]*P[fstpos[y]-fstpos[x]];
	}
}
int seq[N],m=0;
map<ULL,vector<int> > G; 
bool cmp(int x,int y)
{
	return fstpos[x]<fstpos[y];
}
LL sumL[N],sumF[N];
int get(int D)
{
	int l=1,r=m,mid,pos=0;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(fstpos[seq[mid]]<=D)
		{
			pos=mid;
			l=mid+1; 
		}
		else r=mid-1;
	}
	return pos;
}
int main()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	P[0]=1;
	for(int i=1;i<=n;i++)P[i]=P[i-1]*Base;
	for(int i=1;i<=n;i++)Extend(i,s[i]-'a');
	for(int i=2;i<=tot;i++)add(fa[i],i);
	dfs(1);
	LL ans=0;
	for(int i=2;i<=tot;i++)G[H[i]].push_back(i);
	for(auto U:G)
	{
		m=0;
		for(auto u:U.second)seq[++m]=u;
		sort(seq+1,seq+m+1,cmp);
		for(int i=1;i<=m;i++)
		{
			int x=seq[i];
			sumL[i]=sumL[i-1]+len[x]-len[fa[x]];
			sumF[i]=sumF[i-1]+1ll*(len[x]-len[fa[x]])*fstpos[x];
		}
		for(int i=1;i<=m;i++)
		{
			int x=seq[i];
			int l=len[fa[x]]+1,r=len[x];
			int L=get(fstpos[x]-r-1),R=get(fstpos[x]-l);
			ans+=1ll*(sumL[R]-sumL[L])*(fstpos[x]-len[fa[x]])-(sumF[R]-sumF[L]);
		}
	}
	cout<<ans;
	return 0;
} 

标签:869,const,fstpos,int,sum,Codeforces,while,fst,Round
From: https://www.cnblogs.com/jesoyizexry/p/17400381.html

相关文章

  • SMU Spring 2023 Contest Round 3(2023年湘潭大学新生赛)
    ProblemA.签到啦从大到小排序,累加大于行李w时输出下标即可intans;voidsolve(){cin>>n>>m;intans=0;vector<int>a(n);for(inti=0;i<n;i++){cin>>a[i];}sort(a.begin(),a.end());reverse(a.begin......
  • SMU Spring 2023 Contest Round 1
    B.ContestPreparation#include<iostream>#include<cstdio>#include<algorithm>#include<string>#include<cstring>#include<vector>#include<queue>#include<set>#include<map>#defineinf0x3f......
  • SMU Spring 2023 Contest Round 2
    M.DifferentBilling#include<map>#include<set>#include<cmath>#include<queue>#include<cstdio>#include<vector>#include<climits>#include<cstring>#include<cstdlib>#include<......
  • Educational Codeforces Round 148 [Rated for Div. 2]A~C
    A#include<bits/stdc++.h>usingnamespacestd;typedeflonglongLL;constintN=60;charc[N];voidrun(){ scanf("%s",c+1); intn=strlen(c+1); map<char,int>st; st[c[1]]++; for(inti=2;i<=n/2;++i) ......
  • Educational Codeforces Round 148 (Rated for Div. 2) A-D 题解
    比赛地址A.NewPalindrome题意:给一个回文串,判断是否能重新排成另一个回文串Solution存不同对的个数即可voidsolve(){ strings; cin>>s; intn=s.length(); set<char>st; for(inti=0;i<n/2;i++) { st.insert(s[i]); } if(st.size()>1)cout<<"YES\n"; els......
  • Codeforces 1781H1 - Window Signals (easy version)
    很套路的一道题,把F1写了,F2感觉和F1gap不太大就懒得写了/shui首先需要明白大致思路:直接计算\(2^{nm-k}-1\)之所以会算重,是因为对于同一种图案,可能把它放在很多位置都是合法的。那么显然我们需要选一个代表元来把它的贡献唯一化,非常自然的想法就是把它固定在最左上角那个合......
  • Codeforces Round 872 (Div. 2)
    CodeforcesRound872(Div.2)感谢灵茶山艾府A(脑筋急转弯)给一个回文字符串,找出最长的不回文子串。子串可以是不连续的。没有则输出-1;如果全都是一个字母,那就是-1否则是n-1。因为在原来回文的基础上总可以去掉一个使得不回文(前提是不是全部都是一个字母)B(贪心)给出n*m个数,......
  • Codeforces Round 244 (Div. 2) C. Checkposts(tarjan)
    题目链接思路考虑到如果一些点两两都能互相到达,那么这些点中,只要有一个点是安全的,就可以顾及到其他所有点,而这些点就是强连通分量(SCC)。思路很简单,就是每一个强连通分量中的最小值相加得到第一问的解,而第二问就是求每一个强连通分量有几个最小值,相乘得到答案。代码#include<i......
  • 两道倍数codeforces 题 —— 2倍与加减1相关
    目录题目大意题1题2思路题1题2总结题1https://codeforces.com/problemset/problem/520/B题2https://codeforces.com/problemset/problem/710/E题目大意题1一个设备可支持两种操作:将当前数\times2。将当前数-1−1。另外,当设备中的数不是正数时,设备将会崩溃。现在给......
  • Codeforces 543E - Listening to Music(分块)
    根号log做法。能过CF的数据,但过不了校内模拟赛的数据/tuu考虑从\(f(i,x)\)到\(f(i+1,x)\)的变化在哪里:少了个\(a_i\)多了个\(a_{i+m}\),因此显然只有\(x\)在它俩中间才有\(f(i,x)\nef(i+1,x)\),即:\[f(i+1,x)-f(i,x)=\begin{cases}-1&(a_i<x\lea_{i+m})\\1&(a_{i+m......