Loading

noip模拟64

A. 三元组

签到题,树状数组乱写就行了.

A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
	#define ll long long int
	#define ull unsigned ll
	#define lf double
	#define lbt(x) (x&(-x))
	#define mp(x,y) make_pair(x,y)
	#define lb lower_bound 
	#define ub upper_bound
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memcpy(x,y,sizeof x)
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	inline ll read() {
		ll res=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0; 
		while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
		return cit?res:-res;
	}
} using namespace BSS;

const ll N=1e5+21;

ll Ts,n,mod,ans,cnt;

ll vis[N],bin[N];
struct Bit_Array{
	ll res; ll c[N<<1];
	inline void update(ll x,ll w){
		if(x<=0){ c[0]+=w; return ; }
		for(;x<mod;x+=lbt(x)) c[x]+=w;
	}
	inline void del(ll x){
		if(x<=0){ c[0]=0; return ;}
		for(;x<mod;x+=lbt(x)) c[x]=0;
	}
	inline ll query(ll x){
		res=0+c[0]*(x>=0);
		for(;x>0;x&=(x-1)) res+=c[x]; return res;
	}
	inline ll ask(ll l,ll r){
		return query(r)-query(l-1); 
	}
}bit;
signed main(){
	File(exclaim);
	Ts=read(); ll l,r,tmp=0;
	for(int T=1;T<=Ts;T++){
		n=read(),mod=read(),ans=0,cnt=0;
		for(ll i=n;i>=1;i--){
			tmp=i*i*i%mod,bit.update(tmp,1);	
			if(!vis[tmp]) vis[tmp]=1,bin[++cnt]=tmp;
			if(i%mod==0) ans+=bit.ask(0,mod-1)*(i/mod);
			else 
			{
				l=(i*i+1)%mod,r=(i*i+i)%mod;	
				ans+=bit.ask(0,mod-1)*(i/mod);
				if(l<=r) ans+=bit.ask(l,r);
				else ans+=bit.ask(l,mod-1)+bit.ask(0,r);
			}
		}
		for(int i=1;i<=cnt;i++) bit.del(bin[i]),vis[bin[i]]=0;
		printf("Case %d: %lld\n",T,ans);
	}
	exit(0);
}

B. 简单的字符串

不会回文树,也不会 \(manacher\).

考场上就想到了这个题目的所有性质.

但是差一个 \(Border\) 理论 (膜拜这位 \(dalao\),讲得很清晰.)

考后看到题解中说到只会跳两三次,于是觉得暴跳 \(kmp\) 就行了.

虽然这样也能 \(A\),但实际上复杂度是假的,可以被卡成 \(O(n^3)\).

Accepted and Fake
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
	#define ll int
	#define ull unsigned ll
	#define lf double
	#define lbt(x) (x&(-x))
	#define mp(x,y) make_pair(x,y)
	#define lb lower_bound 
	#define ub upper_bound
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memcpy(x,y,sizeof x)
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	inline ll read() {
		ll res=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0; 
		while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
		return cit?res:-res;
	}
} using namespace BSS;

const ll N=6e3+21;
const ull P=131;	

ll m,n,ans;
ll nxt[N][N];
ull pre[N],po[N],sp[N];
inline void PUT(ll l,ll r){ for(int i=l;i<=r;i++) cout<<sp[i]<<' '; puts(""); }
inline ull getsh(ll l,ll r){ 
	return 1u*pre[r]-1u*pre[l-1]*po[r-l+1]; 
}
inline void getnxt(){
	for(int st=1;st<=n;st++){
		for(int i=st-1;i<=n;i++) nxt[st][i]=st-1;
		for(int i=st+1,j=st-1;i<=n;i++){
			while(j>st-1 and sp[i]!=sp[j+1]) j=nxt[st][j];
			if(sp[i]==sp[j+1]) nxt[st][i]=++j;
			else nxt[st][i]=st-1;
		}
	}
}
inline bool check(ll l,ll r){
	ll mid=(l+r)>>1,nowmid=nxt[l][r];
	if(getsh(l,mid)==getsh(mid+1,r)) return 1;
	while(nowmid>mid) nowmid=nxt[l][nowmid];
	while(nowmid>=l){
		if(getsh(nowmid+1,mid)==getsh(mid+1,2*mid-nowmid)) return 1;
		nowmid=nxt[l][nowmid];
	}
	return 0;
}
inline void Work(){
	po[0]=1;
	for(int i=1;i<=n;i++){
		sp[i]=read(),pre[i]=pre[i-1]*P+sp[i];
		po[i]=po[i-1]*P;
	}
	getnxt();
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j+=2){
 			if(nxt[i][j]<i) continue;
			ans+=check(i,j);
		}
	}
}
signed main(){
	File(s);
	n=read(),Work();
	printf("%d\n",ans),exit(0);
}

本以为这样就结束了,但是多亏 \(Yubai\) 来问我这题做法(因为貌似只有我一个人是用 \(kmp\) \(A\) 掉的).

实际上我们完全可以去掉一个 \(n\).

如果会了上面的理论,那么这就是个板子题了,现在正式进入题解.

我们发现答案其实就是由 \(A+B+B+A\) 构成的一个字符串(\(A,B\) 是字符串,\(B\)可以为空).

考虑得到 \(A\)

然后我们在每个子区间 \([l,r]\) 分别处理出来 \(ta\)\(border\).

我们发现这个 \(border\) 长度可能大于 \(\dfrac{r-l+1}{2}\),考虑把 \(ta\) 缩小到一般长度以内.

只要学会上面的理论,这个东西还是很好说的.

设子串 \([l,r]\)\(S\).

由于大于 \(\dfrac{len_s}{2}\)\(Border\) 的长度为一个等差序列。

所以可以求出 \(S\)\(Max\ Border\),然后求出 \(Max\ Border\)\(Max\ Border\) ,设两个 \(Max\ Border\) 分别为 \(M_1,M_2\).

公差就是 \(M_1\ - \ M_2\).

于是 \(A\) 便可以跳公差求出了.

考虑得到 \(B\)

我们可以枚举中点,然后 \(Hash\) 得到中间 \(B\) 的部分.

这里建议结合 \(PPT\) 中给出的题解一起理解.

到此我们得到了 \(A\)\(B\)..

我们得到了最长的 \(A\) 和最长的 \(B\) 之后,余下的我们分别进行判断.

这里都要判断,并不取决于 \(A\)\(B\) 的相对大小而选择舍弃哪一个.

这里有 \(Yubai\) 的手模样例:1 1 1 1 1 1 2 1 1 2 1 1 1 1 1 1 . 样例说明并不能舍弃某种判断.

至此本题结束,并膜掰.

\(upd: 2021.9.29\)

\(Yubai\) 又一次发现了新的问题所在,并给出了证明.

为了防止 \(Yubai\) 遗忘自己的智慧成果,这里决定盘一盘.

\(U=A+B,V=B+A\).

现有两个事实:

  1. \(A\)\(U+V\)\(\le mid\) 的最长 \(Border\).
  2. \(B\)\(V+U\)\(\le mid\) 的最长 \(Border\).

试证明:两个事实中至少一个成立.

我们可以只证明一个,另外一个同理.

如果 \(A\) 不是 \(U+V\) 的最长 \(Border\).

那么 \(U+V\) 的最长 \(Border\) 一定由 \(A+T\) 构成,其中 \(B=T+Z+T\).

现有推论:由于 \(A+T\) 是最长 \(Border\),那么 \(T\) 一定是 \(A\) 的一个整周期.

那么串 \(V+U\) 可以表示为 \(T+Z+T+x*T+x*T+T+Z+T\).

这时,如果 \(B\) 不是串 \(V+U\) 的最长 \(Border\),那么 \(Z\) 一定等于 \(y*T\).

这时与最初 \(A\) 不是 \(U+V\) 的最长 \(Border\) 的假设相悖.

至此证明结束,并再次膜掰.

B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
	#define ll int
	#define ull unsigned ll
	#define lf double
	#define lbt(x) (x&(-x))
	#define mp(x,y) make_pair(x,y)
	#define lb lower_bound 
	#define ub upper_bound
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memcpy(x,y,sizeof x)
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	inline ll read() {
		ll res=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0; 
		while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
		return cit?res:-res;
	}
} using namespace BSS;

const ll N=6e3+21;
const ull P=131;	

ll m,n,ans;
ll nxt[N][N],bd[N][N];
ull pre[N],po[N],sp[N];
inline void PUT(ll l,ll r){ for(int i=l;i<=r;i++) cout<<sp[i]<<' '; puts(""); }
inline ull getsh(ll l,ll r){ 
	return 1u*pre[r]-1u*pre[l-1]*po[r-l+1]; 
}
inline void getnxt(){
	for(int st=1;st<=n;st++){
		for(int i=st-1;i<=n;i++) nxt[st][i]=st-1;
		for(int i=st+1,j=st-1;i<=n;i++){
			while(j>st-1 and sp[i]!=sp[j+1]) j=nxt[st][j];
			if(sp[i]==sp[j+1]) nxt[st][i]=++j;
			else nxt[st][i]=st-1;
		}
	}
}
inline void getbord(){
	for(int mid=1;mid<n;mid++){ // 枚举中点
		ll lmt=min(mid,n-mid);
		for(int l=1;l<=lmt;l++){ 
			bd[mid][l]=bd[mid][l-1];
			if(getsh(mid-l+1,mid)==getsh(mid+1,mid+l)) bd[mid][l]=l;
		}
	}
}
inline bool check(ll l,ll r){
	ll mid=(l+r)>>1,nownxt=nxt[l][r];
	if(nownxt>mid){
		ll nxtnxt=nxt[l][nownxt],delta=nownxt-nxtnxt;
		nownxt=nownxt-delta*((nownxt-mid)/mid);
		if(nownxt>mid) nownxt=nxt[l][nownxt];
	}
	if(nownxt==mid) return 1;
	if(getsh(nownxt+1,mid)==getsh(mid+1,2*mid-nownxt)) return 1;
	ll len=r-mid,nowbd=bd[mid][len],br=mid+nowbd,bl=mid-nowbd+1;
	if(l==bl and r==br) return 1;
	if(getsh(l,bl-1)==getsh(br+1,r)) return 1;
	return 0;
}
signed main(){
	File(s);
	n=read(),po[0]=1;
	for(int i=1;i<=n;i++){
		sp[i]=read();
		pre[i]=pre[i-1]*P+sp[i],po[i]=po[i-1]*P;
	}
	getnxt(),getbord();
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j+=2){
 			if(nxt[i][j]<i) continue;
			ans+=check(i,j);
		}
	}
	printf("%d\n",ans),exit(0);
}

C. 环路

容易写出 \(dp\) 式子,发现写出来之后可以矩阵,每个矩阵主对角线的和就是最终答案.

C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
	#define ll long long int
	#define lf double
	#define ull unsigned ll
	#define mp make_pair
	#define lb lower_bound
	#define ub upper_bound
	#define lbt(x) ((x)&(-(x)))
	#define Fill(x,y) memset(x,y,sizeof(x))
	#define Copy(x,y) memcpy(x,y,sizeof(x))
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	auto read=[]()->ll{
		ll w=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
		while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
		return cit?w:(-w);
	};
} using namespace BSS;

const ll N=105;

ll m,n,mod,ans;
struct Mat{
	ll w[N][N];
	friend Mat operator *(Mat a,Mat b){
		Mat c;
		for(ll i=1;i<=n;i++){
			for(ll j=1;j<=n;j++){
				c.w[i][j]=0;
				for(ll k=1;k<=n;k++)
					c.w[i][j]=(c.w[i][j]+a.w[i][k]*b.w[k][j]%mod)%mod;
			}
		}
		return c;
	}
	friend Mat operator +(Mat a,Mat b){
		for(ll i=1;i<=n;i++){
			for(ll j=1;j<=n;j++)
				a.w[i][j]=(a.w[i][j]+b.w[i][j])%mod;
		}
		return a;
	}
}I,f,g,lst;
void love(ll k){
	if(k==1) return f=g,lst=g,void();
	love(k>>1); f=f*(lst+I),lst=lst*lst;
	if(k&1) lst=lst*g,f=f+lst;		
}
signed main(){
	File(tour);
	n=read(); char s[N];
	for(ll i=1;i<=n;i++){
		scanf("%s",s+1); I.w[i][i]=1;
		for(ll j=1;j<=n;j++) g.w[i][j]=(s[j]=='Y');
	}
	m=read()-1,mod=read(),love(m);
	for(ll i=1;i<=n;i++) ans=(ans+f.w[i][i])%mod;
	printf("%lld\n",ans),exit(0);
}

D. 过河

读错题,直接死.

发现肯定要有一头猪和任何猪都有矛盾,这里将其命名为神猪.

第一步一定是先运送神猪过去.

对剩下的猪进行连边,发现如果是个二分图一定有解.

一开始或许认为不是二分图就不行,发现并不是这样的,因为还有一个东西叫做 小奇 .

对于形成三元环的猪,并不是不能在一起,但是与此同时小奇必须和所有已存在的三元环在一起.

考虑如何使用小奇,发现剩下的猪连边之后再删掉两个点,如果是个二分图那么有解.

于是 \(O(n)\) 枚举删掉的第一个猪,再 \(O(n)\)\(dfs\) 序上判断是否合法就好了.

D_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
	#define ll long long 
	#define ull unsigned ll
	#define lf long double
	#define lbt(x) ((x)&(-(x)))
	#define mp(x,y) make_pair(x,y)
	#define lb lower_bound 
	#define ub upper_bound
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memcpy(x,y,sizeof x)
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	inline ll read() {
		ll w=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0; 
		while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
		return cit?w:-w;
	}
} using namespace BSS;

const ll N=1e3+21,M=3e3+21;

ll m,n,ts,god,king,odds;
ll vis[N],dep[N],odd[N],head[N],cfo[N],cfe[N],fa[N],son[N],ans[N];
ll id[M][3];
struct I { ll u,v,nxt; } e[M<<1];
auto add=[](ll u,ll v)->void{
	e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u];
	head[u]=ts;
};
auto PreWork=[]()->void{
	king=0;
	fill(vis,vis+n+5,0);
};
void dfs(ll u,ll dad){
	fa[u]=dad,dep[u]=dep[dad]+1;
	for(ll i=head[u];i;i=e[i].nxt){
		if(e[i].v==dad) continue;
		if(dep[e[i].v]){
			if(dep[e[i].v]>=dep[u]) continue;
			ll len=dep[u]-dep[e[i].v]+1;
			if(len&1){
				odd[u]++,odd[fa[e[i].v]]--,odds++;
				cfo[u]++,cfo[son[e[i].v]]--;
			}
			else{
				cfe[u]++,cfe[son[e[i].v]]--;
			}
		}
		else{
			son[u]=e[i].v,dfs(e[i].v,u);
			if(cfe[e[i].v] and cfo[e[i].v]) ans[u]=-1;
			cfe[u]+=cfe[e[i].v],cfo[u]+=cfo[e[i].v],odd[u]+=odd[e[i].v];
		}		
	}
}
auto love=[](ll x)->bool{
	if(x==god) return 0;
	ll u,v; ts=0,odds=0;
	for(ll i=1;i<=n;i++){
		odd[i]=0,cfo[i]=0,cfe[i]=0,ans[i]=0;
		head[i]=0,fa[i]=0,dep[i]=0,son[i]=0;
	}
	for(ll i=1;i<=m;i++){
		u=0,v=0;
		for(ll j=0;j<3;j++){
			if(id[i][j]==god) continue;
			u ? v=id[i][j] : u=id[i][j];
		}
		if(u==x or v==x) continue;
		add(u,v),add(v,u);
	}
	for(ll i=1;i<=n;i++){
		if(i==x or i==god) continue;
		if(!dep[i]) dfs(i,0);
	}
	for(ll i=1;i<=n;i++){
		if(i==x or i==god or ans[i]<0) continue;
		if(odd[i]==odds) return 1;
	}
	return 0;	
};
auto Work=[]()->void{
	n=read(),m=read(),PreWork(); ll flag=0;
	for(ll i=1;i<=m;i++){
		for(ll j=0;j<3;j++) vis[id[i][j]=read()]++;
	}
	for(ll i=1;i<=n;i++)flag+=(vis[i]==m);
	if(!flag) { puts("no"); return ; }
	if(flag>=2) { puts("yes"); return ; }
	for(ll i=1;i<=n;i++) if(vis[i]==m) god=i;
	for(ll i=1;i<=n;i++) if(love(i)){ puts("yes"); return ; }
	puts("no"); return ;
};
signed main(){
	File(river);
	for(int Ts=read();Ts;Ts--) Work();
	exit(0);
}
posted @ 2021-09-28 20:19  AaMuXiiiiii  阅读(49)  评论(0编辑  收藏  举报