「Set·Read·题目交流通道·题目难度提升」

A. Set

维护一个前缀和就行了.

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=1e6+21;

ll n,alls,cnt;
ll pre[N],vis[N],val[N];
signed main(){
	File(a);
	n=read(); vis[0]=0;
	for(int i=1;i<=n;i++){
		pre[i]=(pre[i-1]+read())%n;
		if(vis[pre[i]]){
			cout<<i-vis[pre[i]]<<endl;
			for(int j=vis[pre[i]]+1;j<=i;j++){
				cout<<j<<' ';
			}
			exit(0);
		}
		vis[pre[i]]=i;
	}
	exit(0);
}

B. Read

发现如果没有同样的书出现次数超过\(\frac{(n+1)}{2}\)

那么其实是不需要删掉的.

如果出现了,也很容易减掉.

所以关键在于怎么求出出现次数最多的书,之后再扫一遍就可以.

题解中给出了一种很刁钻的写法.

用两个变量 \(id\) , \(cnt\) .

\(cnt\) 初始为 \(0\),然后生成每一个 \(A_i\),如果 \(cnt==0\), 那么就
\(id=A_i\)\(cnt=1\).

否则如果 \(id==A_i\), 则\(cnt++\),如果不等于,则 \(cnt--\).

B_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=1e3+21;

ll n,m,nowcol,len,ans,cnt;
ll cont[N],X[N],Y[N],Z[N],sp[N];
signed main(){
	File(b);
	int M=read(),K=read(),S=(1<<K)-1;
	for(int i=1;i<=M;i++) n+=(cont[i]=read());
	for(int i=1;i<=M;i++) X[i]=read();
	for(int i=1;i<=M;i++) Y[i]=read();
	for(int i=1;i<=M;i++) Z[i]=read();
	for(int i=1;i<=M;i++){
		ll lst=X[i];
		if(!cnt) nowcol=lst;
		if(lst==nowcol) cnt++; else cnt--;
		for(int j=1;j<cont[i];j++){
			lst=(lst*Y[i]+Z[i])&S;
			if(!cnt) nowcol=lst;
			if(lst==nowcol) cnt++; else cnt--;
		}
	}
	cnt=0;
	for(int i=1;i<=M;i++){
		ll lst=X[i];
		if(lst==nowcol) cnt++;
		for(int j=1;j<cont[i];j++){
			lst=(lst*Y[i]+Z[i])&S;
			if(lst==nowcol) cnt++;
		}
	}
	if(cnt>(n+1)/2) printf("%lld\n",2*cnt-n-1);	
	else puts("0");
	exit(0);
}

C. 题目交流通道

有两个关键的性质.

  1. 如果两个点之间的最小距离要求为 \(0\),那么 \(ta\) 们就可以看成一个点.

  2. 如果 \(d_{i,j}==d_{i,k}+d{k,j}\) ,那么 \(path_{i,j}\) 就是废弃的,可以随便选.

然后考虑容斥选就可以了.

C_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=1009,mod=998244353;

ll m,n,ans,cnt;
ll g[N],f[N],fa[N],frc[N],bin[N],son[N];
ll req[N][N],dis[N][N],die[N][N],add[N][N];
inline ll DIE(){ puts("0"),exit(0); }
inline ll find(ll x){ return fa[x]==x ? x : fa[x]=find(fa[x]); }
inline ll ksm(ll a,ll b,ll c){
	ll res=1; a%=c;
	for(;b;a=a*a%c,b>>=1) if(b&1) res=res*a%c;
	return res%c;
}	
inline ll C(ll a,ll b){
	if(a>b) return 0;
	return frc[b]*ksm(frc[a]*frc[b-a],mod-2,mod)%mod;
}
signed main(){
	File(c); 
	n=read(),m=read(),ans=1;
	frc[0]=1; for(int i=1;i<=N-9;i++) frc[i]=frc[i-1]*i%mod,fa[i]=i;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			req[i][j]=read(); if(req[i][j]>m) DIE();
		}
		if(req[i][i]) DIE();
	}
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++){
			if(i==k) continue;
			for(int j=1;j<=n;j++){
				if(j==k) continue;
				if(req[i][j]>req[i][k]+req[k][j]) DIE();
			}
		}
	for(int i=1;i<=n;i++){
		g[i]=ksm(m+1,(i*(i-1))>>1ll,mod),f[i]=g[i];
		for(int j=1;j<i;j++)
			f[i]=(f[i]-f[j]*g[i-j]%mod*C(j-1,i-1)%mod*ksm(m,j*(i-j),mod)%mod+mod)%mod;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(!req[i][j]){
				if(find(i)!=find(j)) fa[find(i)]=find(j);
			}
			if(req[i][j]!=req[j][i]) DIE();
		}
	}
	for(int i=1;i<=n;i++){
		son[find(i)]++;
		if(find(i)==i) bin[++cnt]=i;
	}		
	for(int k=1;k<=cnt;k++){
		for(int i=1;i<=cnt;i++){
			if(k==i) continue;
			for(int j=1;j<=cnt;j++){
				if(k==j or i==j) continue;
				if(req[bin[i]][bin[j]]==req[bin[i]][bin[k]]+req[bin[k]][bin[j]]){
					add[bin[i]][bin[j]]=1,add[bin[j]][bin[i]]=1;
				}
			}
		}
	}
	for(int i=1;i<=cnt;i++){
		ans=ans*f[son[bin[i]]]%mod;
		for(int j=i+1;j<=cnt;j++)
			if(!add[bin[i]][bin[j]]){
				ans=ans*(ksm(m-req[bin[i]][bin[j]]+1,son[bin[i]]*son[bin[j]],mod)-ksm(m-req[bin[i]][bin[j]],son[bin[i]]*son[bin[j]],mod)+mod)%mod;
			}
			else{
				ans=ans*ksm(m-req[bin[i]][bin[j]]+1,son[bin[i]]*son[bin[j]],mod)%mod;
			}
	}
	printf("%lld\n",ans);
	exit(0);
}

D. 题目难度提升

容易发现如果整个序列中没有相同的数,那么中位数一定严格单调上升.

这个时候根据要求去放就可以了.

如果有相同的数,那么对策略产生影响的也只是比最终序列的中位数更小的那些相同的数.

不知道为什么,只写最终序列的中位数相等就能过.

不过打表发现好像确实只写最终序列的中位数相等就行..?

又容易发现如果最终序列的中位数出现了不只一次,那么整个序列的中位数是不会变的.

这个时候贪心做就可以.