义乌集训7.13 contest 6题解

2021.7.13 Contest 题解

T1:

Description:

胖教授和瘦教授在下中国象棋。胖教授有 (n_1) 个车,瘦教授有 (n_2) 个车。车都在整点坐标上。一个车被另一个车攻击需要满足:

  1. 这两个车来自于不同的教授;
  2. 它们的 (x) 坐标相同或者 (y) 坐标相同;
  3. 在它们之间没有其他车;

请帮助两位教授算算哪些车是被攻击的。

Input:

​ 第一行两个整数 (n_1,n_2)
​ 接下来 (n_1) 行,每行两个整数 ((x,y)) 表示胖教授的车的坐标。
​ 接下来 (n_2) 行,每行两个整数 ((x,y)) 表示瘦教授的车的坐标。
​ 数据保证车的位置两两不同。

Output:

第一行输出一个长度为 (n_1) 的字符串,第 (i) 位为 (1) 表示胖教授的第 (i) 个车被攻击了,否则第 (i) 位为 (0)

第二行输出一个长度为 (n_2) 的字符串,第 (i) 位为 (1) 表示瘦教授的第 (i) 个车被攻击了,否则第 (i) 位为 (0)

Sample1 Input:

3 2
0 0
0 1
1 0
0 -1
-1 0

Sample1 Output:

100
11

Hint:

(1leq n_1,n_2leq200000,−10^9≤x,y≤10^9)

题目分析:

​ 随便乱搞都能过,这里我采用了离散化的方法。当然,(set) 也能搞。

代码如下(马蜂很丑,不喜勿喷)——

#include<bits/stdc++.h>
#define N 200005
#define LL long long
using namespace std;
int n,m,S,tot,rk1[N],rk2[N],X1[N],Y1[N],X2[N],Y2[N];bool b[N<<2],vis1[N],vis2[N];vector<int> g;
inline bool cmp1(int x,int y){if(X1[x]^X1[y]) return X1[x]<X1[y];return Y1[x]<Y1[y];}
inline bool cmp2(int x,int y){if(X2[x]^X2[y]) return X2[x]<X2[y];return Y2[x]<Y2[y];}
inline void solve(){
	memset(b,0,sizeof(b)),sort(rk1+1,rk1+n+1,cmp1),sort(rk2+1,rk2+m+1,cmp2);int now=1;for(register int i=1;i<=n;i++)
	{int X=X1[rk1[i]];while(now<=m&&X2[rk2[now]]<X) b[Y2[rk2[now]]]=1,now++;if(b[Y1[rk1[i]]]) vis1[rk1[i]]=1;b[Y1[rk1[i]]]=0;}
	memset(b,0,sizeof(b)),now=1;for(register int i=1;i<=m;i++)
	{int X=X2[rk2[i]];while(now<=n&&X1[rk1[now]]<X) b[Y1[rk1[now]]]=1,now++;if(b[Y2[rk2[i]]]) vis2[rk2[i]]=1;b[Y2[rk2[i]]]=0;}
}
inline int read(){int ret=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();return ret*f;}
int main(){
//	freopen("eat.in","r",stdin);freopen("eat.out","w",stdout);
	n=read(),m=read();for(register int i=1;i<=n;i++) rk1[i]=i,X1[i]=read(),Y1[i]=read(),g.push_back(X1[i]),g.push_back(Y1[i]);
	for(register int i=1;i<=m;i++) rk2[i]=i,X2[i]=read(),Y2[i]=read(),g.push_back(X2[i]),g.push_back(Y2[i]);sort(g.begin(),g.end()),g.erase(unique(g.begin(),g.end()),g.end());S=g.size();
	for(register int i=1;i<=n;i++) X1[i]=lower_bound(g.begin(),g.end(),X1[i])-g.begin()+1,Y1[i]=lower_bound(g.begin(),g.end(),Y1[i])-g.begin()+1;
	for(register int i=1;i<=m;i++) X2[i]=lower_bound(g.begin(),g.end(),X2[i])-g.begin()+1,Y2[i]=lower_bound(g.begin(),g.end(),Y2[i])-g.begin()+1;
	solve();for(register int i=1;i<=n;i++) X1[i]=S+1-X1[i];for(register int i=1;i<=m;i++) X2[i]=S+1-X2[i];
	solve();for(register int i=1;i<=n;i++) swap(X1[i],Y1[i]);for(register int i=1;i<=m;i++) swap(X2[i],Y2[i]);
	solve();for(register int i=1;i<=n;i++) X1[i]=S+1-X1[i];for(register int i=1;i<=m;i++) X2[i]=S+1-X2[i];solve();
	for(register int i=1;i<=n;i++) if(vis1[i]) putchar('1');else putchar('0');putchar('
');
	for(register int i=1;i<=m;i++) if(vis2[i]) putchar('1');else putchar('0');return 0;
}

T2:

Description:

给定串 (s) ,问其中有多少 (namomo) 子序列。 定义一个子序列 (t)(namomo) 子序列,当且仅当

  1. (t_3=t_5)
  2. (t_4=t_6)
  3. (t_1,t_2,t_3,t_4) 两两不同。

Input:

​ 一行一个字符串 (s)

(s) 仅包含小写字母 ((a-z))、大写字母 ((A-Z))、数字 ((0-9))

Output:

​ 输出一行一个整数表示答案——(namomo) 子序列的数目。请输出答案 mod (998244353)

Sample1 Input:

wohaha

Sample1 Output:

1

Sample2 Input:

momomo

Sample2 Output:

0

Sample3 Input:

gshfd1jkhaRaadfglkjerVcvuy0gf

Sample3 Output:

73

Sample4 Input:

retiredMiFaFa0v0

Sample4 Output:

33

Hint:

(6leq∣s∣leq 10^6)

题目分析:

​ 我们把 (namomo) 序列分为两段 (t_1,t_2)(t_3,t_4,t_5,t_6)

​ 显然,如果我们确定了 (t_3,t_4,t_5,t_6) 那么合法的 (t_1,t_2) 的组数可以用前缀和之类的 trick 直接算出。

​ 我们不妨枚举 (t_3) ,我们可以得到合法的 (t_5) 的位置,我们再枚举 (t_4) ,我们可以得到合法的 (t_6) 的位置,于是这道题用 (O(26n)) 就能通过。

代码如下(马蜂很丑,不喜勿喷)——

#include<bits/stdc++.h>
#define N 1000005
#define LL long long
using namespace std;
const int p=998244353;
int n,tmp,tt,pre[N][65],a[N],mp[1000],Lst[N],lst[65]/*,nxt[N],fir[65]*/,ans,sum1[65],sum2[65];LL S[N];char s[N];inline int get(int x,int y,int z){return (S[x]-1ll*(x-y-z)*(y+z)-1ll*y*z)%p;}
inline int read(){int ret=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();return ret*f;}
int main(){
//	freopen("string.in","r",stdin);freopen("string.out","w",stdout);
	scanf("%s",s+1);for(register int i='a';i<='z';i++) mp[i]=i-'a';for(register int i='A';i<='Z';i++) mp[i]=i-'A'+26;for(register int i='0';i<='9';i++) mp[i]=i-'0'+52;
	n=strlen(s+1);for(register int i=1;i<=n;i++){a[i]=mp[s[i]];for(register int j=0;j^62;j++) pre[i][j]=pre[i-1][j];pre[i][a[i]]++;/*if(!lst[a[i]]) fir[a[i]]=i;*/Lst[i]=lst[a[i]]/*,nxt[lst[a[i]]]=i*/,lst[a[i]]=i;}
	for(register int i=1;i<=n;i++) S[i]=S[i-1]+1ll*(i-pre[i][mp[s[i]]]);for(register int x=0;x^62;x++) for(register int j=1,i=a[j];j<=n;j++,i=a[j]) if(i^x){
		tt=pre[j-1][x];if(!Lst[j]) sum1[i]=get(j-1,tt,pre[j-1][i]),sum2[i]=1ll*sum1[i]*tt%p;
		else ans=((LL)ans+1ll*(pre[n][x]-tt)*(1ll*tt*sum1[i]%p-sum2[i]+p))%p,tmp=get(j-1,tt,pre[j-1][i]),sum1[i]+=tmp,(sum1[i]>=p)&&(sum1[i]-=p),sum2[i]=((LL)sum2[i]+1ll*tmp*tt)%p;
	}
	cout<<ans<<'
';return 0;
}

T3:

Description:

​ 给定一棵大小为 (n) 的有根树。每个节点有个非负权值,对于每个节点 (x),我们找到包含 (x) 的节点的平均值最大的连通块,定义 (f(x)) 为这个平均值。请求出 (min({f(i)│i∈{1,2,…,n} }))

Input:

​ 输入第一行一个正整数 (n)。 接下来一行 (n) 个整数 (a_1 , a_2 ,…, a_n)(a_i) 表示第 (i) 个点的权值。 接下来一行 (n-1) 个整数 (p_1 , p_2 ,… , p_{n-1} (1leq p_ileq i))(p_i) 表示第 (i+1) 个点的父节点编号。

Output:

​ 输出一行一个数表示答案。绝对误差或相对误差在 ({10}^{-6}) 内即为正确。

Sample1 Input:

5
0 1 0 1 0
1 1 2 2

Sample1 Output:

0.5

Hint:

对于全部数据,(2 leq n leq 10^5,0leq a_i leq 10^8.)

题目分析:

​ 分数规划+换根DP的套路题。考虑二分答案,将每个数的权值减去这个数,然后跑换根DP求出经过每个点的最大连通块,若每个连通块的权值都大于等于 (0),则 (L=mid);反之,(R=mid)。由于数据过水,(eps) 调到 (70) 都能过!!!当然,正经人 (eps) 调到 (10^{-7})即可。

代码如下(马蜂很丑,不喜勿喷)——

#include<bits/stdc++.h>
#define N 100005
#define LL long long
#define inf 100000000
#define DB double
#define eps 70
using namespace std;
int n,tot,fa[N],fir[N],nxt[N],son[N];DB a[N],b[N],f[N],g[N],L=inf,R=0;bool flg;
inline void dfs(int x){f[x]=b[x];for(register int i=fir[x];i;i=nxt[i]) dfs(son[i]),f[x]+=max(f[son[i]],0.0);}
inline void dfs2(int x){if(g[x]<0.0) flg=1;for(register int i=fir[x];i;i=nxt[i]) g[son[i]]=max(0.0,g[x]-max(f[son[i]],0.0))+f[son[i]],dfs2(son[i]);}
inline bool check(DB x){flg=0;for(register int i=1;i<=n;i++) b[i]=a[i]-x;dfs(1),g[1]=f[1],dfs2(1);return flg;} inline void add(int x,int y){son[++tot]=y,nxt[tot]=fir[x],fir[x]=tot;}
inline int read(){int ret=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();return ret*f;}
int main(){
	n=read();for(register int i=1;i<=n;i++) a[i]=read(),L=min(L,a[i]),R=max(R,a[i]);for(register int i=2,x;i<=n;i++) x=read(),add(x,i);
	while(R-L>eps){DB mid=(L+R)/2.0;if(check(mid)) R=mid;else L=mid;}printf("%0.9lf
",R);return 0;
}

T4:

Description:

​ 给定序列 (a_1,a_2,…,a_n)(m) 组询问。

​ 对于每组询问,给定区间 ([l,r] (1le lle rle n)),请问有多少子区间 ([i,j]) 满足 (lle ile jle r) 并且子序列 (a_i,…,a_j) 中不同的整数的数目是奇数。

Input:

​ 第一行一个整数 (n)
​ 接下来一行 (n) 个整数 (a_1,a_2,…,a_n)
​ 接下来一行一个整数 (m)
​ 接下来 (m) 行每行两个整数 (l,r)

Output:

​ 对于每组询问,输出一行一个整数表示答案。

Sample1 Input:

5
1 2 3 2 1
5
1 5
2 4
1 3
2 5
4 4

Sample1 Output:

10
3
4
6
1

Sample2 Input:

5
2 3 5 1 5
5
2 3
1 1
1 3
2 5
2 4

Sample2 Output:

2
1
4
6
4

Sample3 Input:

10
2 8 5 1 10 5 9 9 3 5
10
6 8
1 2
3 5
5 7
1 7
3 9
4 9
1 4
3 7
2 5

Sample3 Output:

4
2
4
4
16
16
12
6
9
6

Hint:

对于 (20\%) 的数据,满足 (n,mle 100)

对于 (100\%) 的数据,满足 (1le n,mle 5 imes 10^5)(1le a_ile n)(1 le l le r le n)

题目分析:

​ 这道题有多种维护的方法,当然都是离线后用线段树搞的(其实在线做也可以

​ 我们不妨让询问按照 (r) 从小到大排序,我们只需统计每加入一个 (i) 后答案的变化。我们记下每一个 (i)(a_i) 上一次出现的位置 (pre_i),则加入 (i) 之后,区间 ([pre_i+1,i]) 不同整数数目的奇偶性会发生改变,在线段树上打标记即可。

​ 具体实现起来需要维护的标记数目比较多,我写得相对比较繁琐,就不详细展开说了,详情见代码。

代码如下(马蜂很丑,不喜勿喷)——

#include<bits/stdc++.h>
#define N 500005
#define LL long long
using namespace std;
int n,m,bl,L,R,a[N],lst[N],pre[N],nxt[N],T[N<<2],sum1[N<<2],sum2[N<<2],tag1[N<<2],tag2[N<<2];LL res,ans[N],S1[N<<2],S2[N<<2];
inline void swap(int &x,int &y){x^=y,y^=x,x^=y;} struct node{int l,r,id;}p[N];inline bool cmp(const node x,const node y){return x.r<y.r;}
inline void PU(int x){S1[x]=S1[x<<1]+S1[x<<1|1],S2[x]=S2[x<<1]+S2[x<<1|1],sum1[x]=sum1[x<<1]+sum1[x<<1|1],sum2[x]=sum2[x<<1]+sum2[x<<1|1];} inline void PD(int x){
	if(!T[x<<1]) tag2[x<<1]+=tag2[x],tag1[x<<1]+=tag1[x];else tag1[x<<1]+=tag2[x],tag2[x<<1]+=tag1[x];if(!T[x<<1|1]) tag1[x<<1|1]+=tag1[x],tag2[x<<1|1]+=tag2[x];else tag1[x<<1|1]+=tag2[x],tag2[x<<1|1]+=tag1[x];
	S1[x<<1]+=1ll*sum1[x<<1]*tag1[x]+1ll*sum2[x<<1]*tag2[x],S2[x<<1]+=1ll*sum1[x<<1]*tag2[x]+1ll*sum2[x<<1]*tag1[x],S1[x<<1|1]+=1ll*sum1[x<<1|1]*tag1[x]+1ll*sum2[x<<1|1]*tag2[x],S2[x<<1|1]+=1ll*sum1[x<<1|1]*tag2[x]+1ll*sum2[x<<1|1]*tag1[x];
	if(T[x]) T[x<<1]^=T[x],T[x<<1|1]^=T[x],swap(sum1[x<<1],sum2[x<<1]),swap(sum1[x<<1|1],sum2[x<<1|1]);T[x]=tag1[x]=tag2[x]=0;
}
inline void U(int x,int l,int r,int ll,int rr){if(ll>rr) return;if(l>=ll&&r<=rr){swap(sum1[x],sum2[x]),S1[x]+=sum1[x],S2[x]+=sum2[x];if(!T[x]) tag2[x]++;else tag1[x]++;T[x]^=1;return;}PD(x);int mid=l+r>>1;if(mid>=ll) U(x<<1,l,mid,ll,rr);if(mid<rr) U(x<<1|1,mid+1,r,ll,rr);PU(x);}
inline void M(int x,int l,int r,int ll,int rr){if(ll>rr) return;if(l>=ll&&r<=rr){S1[x]+=sum1[x],S2[x]+=sum2[x];if(!T[x]) tag1[x]++;else tag2[x]++;return;}PD(x);int mid=l+r>>1;if(mid>=ll) M(x<<1,l,mid,ll,rr);if(mid<rr) M(x<<1|1,mid+1,r,ll,rr);PU(x);}
inline void B(int x,int l,int r){if(l==r){S1[x]=1,sum1[x]=1;return;}int mid=l+r>>1;B(x<<1,l,mid),B(x<<1|1,mid+1,r);PU(x);}
inline LL Q(int x,int l,int r,int ll,int rr){if(l>=ll&&r<=rr) return S1[x];PD(x);int mid=l+r>>1;LL sum=0;if(mid>=ll) sum=Q(x<<1,l,mid,ll,rr);if(mid<rr) sum+=Q(x<<1|1,mid+1,r,ll,rr);return sum;}
struct FastIO{
	static const int S=1048576;
	char buf[S],*L,*R;int stk[20],Top;~FastIO(){clear();}
	inline char nc(){return L==R&&(R=(L=buf)+fread(buf,1,S,stdin),L==R)?EOF:*L++;}inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	inline void pc(char ch){Top==S&&(clear(),0);buf[Top++]=ch;}inline void endl(){pc('
');}
	FastIO& operator >> (char&ch){while(ch=nc(),ch==' '||ch=='
');return *this;}
	template<typename T>FastIO& operator >> (T&ret){
		ret=0;int f=1;char ch=nc();while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=nc();}
		while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=nc();}ret*=f;return *this;
	}
	FastIO& operator >> (char* s){int Len=0;char ch=nc();while(ch!='
'){*(s+Len)=ch;Len++;ch=nc();}}
	template<typename T>FastIO& operator << (T x){
		if(x<0){pc('-');x=-x;}do{stk[++stk[0]]=x%10;x/=10;}while(x);
		while(stk[0]) pc('0'+stk[stk[0]--]);return *this;
	}
	FastIO& operator << (char ch){pc(ch);return *this;}
	FastIO& operator << (string str){int Len=str.size()-1;for(stk[0]=0;Len>=0;Len--) stk[++stk[0]]=str[Len];while(stk[0]) pc(stk[stk[0]--]);return *this;}
}fin,fout;
int main(){
	fin>>n;for(register int i=1;i<=n;i++) fin>>a[i],pre[i]=lst[a[i]],lst[a[i]]=i;fin>>m;for(register int i=1;i<=m;i++) fin>>p[i].l>>p[i].r,p[i].id=i;sort(p+1,p+m+1,cmp);B(1,1,n);int now=1;
	for(register int i=1;i<=m;i++){while(now<=p[i].r) M(1,1,n,1,pre[now]),U(1,1,n,pre[now]+1,now-1),now++;ans[p[i].id]=Q(1,1,n,p[i].l,p[i].r);}for(register int i=1;i<=m;i++) fout<<ans[i]<<'
';return 0;
}
原文地址:https://www.cnblogs.com/jiangxuancheng/p/15046108.html