Diorvh

导航

2019ACM-ICPC徐州网络赛(9.7)

成绩:

rk451,校内排名6。A了7道,我写了CDEK,大佬写了BG,艾继涛写了M。总体表现很差,从3点开始持续宕机到最后,大佬终于2h自学完回文自动机。不但直接与CCPC拜拜,甚至摧毁了自己的自信心。

A. Who is better

比较裸的数学题。首先中国剩余定理解线性同余方程组。之后斐波那契博弈,目前还没来得及看。还没做出来。

代码:待补...

B. so easy

大佬写的...似乎使用权值线段树维护的。复杂度O(n)。

代码:

#include<bits/stdc++.h>
#define inf 1000000000
#define N 1000000 
using namespace std;
inline char nc()
{
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
	int x=0,b=1;
	char c=nc();
	for(;!(c<='9'&&c>='0');c=nc())if(c=='-')b=-1;
	for(;c<='9'&&c>='0';c=nc())x=x*10+c-'0';
	return x*b;
}
int n,q;
struct kk{
	int op,x;
}Q[N+5];
int cnt,tmp;
int A[2*N+5];
struct tree{
	int l,r,mn;
}T[8*N+5];
inline void pushup(int rt)
{
	T[rt].mn=min(T[rt*2].mn,T[rt*2+1].mn);
}
inline void build(int rt,int l,int r)
{
	T[rt].l=l,T[rt].r=r;
	if(T[rt].l==T[rt].r){
		T[rt].mn=l;
		return;
	}
	int mid=(T[rt].l+T[rt].r)/2;
	build(rt*2,l,mid);
	build(rt*2+1,mid+1,r);
	pushup(rt);
}
inline void modify(int rt,int pos,int x)
{
	if(T[rt].l==pos&&T[rt].r==pos){
		T[rt].mn=x;
		return;
	}
	int mid=(T[rt].l+T[rt].r)/2;
	if(pos<=mid)modify(rt*2,pos,x);
	else modify(rt*2+1,pos,x);
	pushup(rt);
}
inline int query(int rt,int l,int r)
{
	if(T[rt].l==l&&T[rt].r==r){
		return T[rt].mn;
	}
	int mid=(T[rt].l+T[rt].r)/2;
	if(r<=mid)return query(rt*2,l,r);
	else if(l>mid)return query(rt*2+1,l,r);
	else return min(query(rt*2,l,mid),query(rt*2+1,mid+1,r));
}
int main()
{
	//freopen("in.txt","r",stdin);
	n=read(),q=read();
	for(int i=1;i<=q;i++){
		Q[i].op=read();
		Q[i].x=read();
	}
	for(int i=1;i<=q;i++){
		cnt++;
		A[cnt]=Q[i].x;
		cnt++;
		A[cnt]=Q[i].x+1;
	}
	sort(A+1,A+cnt+1);
	int tmp=unique(A+1,A+cnt+1)-A-1;
	build(1,1,tmp);
	for(int i=1;i<=q;i++){
		int x=lower_bound(A+1,A+tmp+1,Q[i].x)-A;
		if(Q[i].op==1){
			modify(1,x,inf);
		}else{
			int ans=query(1,x,tmp);
			printf("%d\n",A[ans]);
		}
	} 
	return 0;
}

C. Buy Watermelon

思博题,更思博的是出题人的英语水平。题意是给定w,问w是否能分成两份,使得两份都是偶数。出题人根本不会英语。若w是奇数或w=2,则不能,否则能。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)/2)
#define ld long double
#define F first
#define S second
#define ms(x,y) memset(x,y,sizeof(x))
#define F1(i,n) for(int i=1;i<=n;++i)
#define F0(i,n) for(int i=0;i<n;++i)
#define vi vector<int>
#define pii pair<int,int>
#define pb push_back
#define rd(a) scanf("%d",&a)
#define rld(a) scanf("%I64d",&a)
#define rs(a) scanf("%s",a)
const int MAX=4e4,P=1e9+7;
int main(){
    int w;
    scanf("%d",&w);
    if (w%2==1||w==2)
        printf("NO\n");
    else
        printf("YES\n");
    return 0;
}

D. Carneginon

比较裸的KMP。分情况匹配即可。注意每次都要先对模式串make_fail()一下。复杂度O(n)

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)/2)
#define ld long double
#define F first
#define S second
#define ms(x,y) memset(x,y,sizeof(x))
#define F1(i,n) for(int i=1;i<=n;++i)
#define F0(i,n) for(int i=0;i<n;++i)
#define vi vector<int>
#define pii pair<int,int>
#define pb push_back
#define rd(a) scanf("%d",&a)
#define rld(a) scanf("%I64d",&a)
#define rs(a) scanf("%s",a)
const int MAX=1e5+20,P=1e9+7;
char s[MAX],t[MAX];
int fail[MAX];
void make_fail(char *p){
    ms(fail,0);
    for (int i=1,j=0;p[i];++i){
        while (j&&p[i]!=p[j])
            j=fail[j-1];
        if (p[i]==p[j])
            fail[i]=++j;
        else
            fail[i]=0;
    }
}
bool kmp(char *s,char *p){
    for (int i=0,j=0;s[i];++i){
        while(j&&s[i]!=p[j])
            j=fail[j-1];
        if (s[i]==p[j]&&!p[++j])
            return true;
    }
    return false;
}
int main(){
    scanf("%s",t);
    int n,lent=strlen(t);
    scanf("%d",&n);
    F1(z,n){
        scanf("%s",s);
        int lens=strlen(s);
        if (lens<lent){
            make_fail(s);
            if (kmp(t,s))
                printf("my child!\n");
            else
                printf("oh, child!\n");
        }
        else if (lens>lent){
            make_fail(t);
            if (kmp(s,t))
                printf("my teacher!\n");
            else
                printf("senior!\n");
        }
        else{
            make_fail(t);
            if (kmp(s,t))
                printf("jntm!\n");
            else
                printf("friend!\n");
        }
    }
    return 0;
}

E. XKC's basketball team

题意是要对每个人,找到最后一个让他angry的人的位置。我的做法是ST表+二分区间。

首先ST表维护区间最大值。之后枚举每个人i,对i+1到n区间进行二分:每次先看右区间,若右区间max>=m+w[i],则最后一个让他angry的人必然在右区间,选择右边。否则,选择左区间。注意有可能右边没有让他angry的人,所以二分之前先检查一下i+1到n区间的最大值是否>=m+w[i],若不是,直接输出-1。复杂度O(nlogn)。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r+1)/2)
#define ld long double
#define F first
#define S second
#define ms(x,y) memset(x,y,sizeof(x))
#define F1(i,n) for(int i=1;i<=n;++i)
#define F0(i,n) for(int i=0;i<n;++i)
#define vi vector<int>
#define pii pair<int,int>
#define pb push_back
#define rd(a) scanf("%d",&a)
#define rld(a) scanf("%I64d",&a)
#define rs(a) scanf("%s",a)
const int MAX=5e5+20,P=1e9+7;
int mi2[35],logg[MAX],n,f[MAX][35];
void initst1(){
    mi2[0]=1,logg[0]=-1;
    F1(i,32)
        mi2[i]=mi2[i-1]*2;
    F1(i,n)
        logg[i]=logg[i/2]+1;
}
void initst2(){
    F1(i,logg[n])
        F1(j,n+1-mi2[i])
            f[j][i]=max(f[j][i-1],f[j+mi2[i-1]][i-1]);
}
int queryst(int l,int r){
    int t=r-l+1,lg=logg[t];
    return max(f[l][lg],f[r-mi2[lg]+1][lg]);
}
int ans[MAX];
int main(){
    int m;
    scanf("%d%d",&n,&m);
    F1(i,n)
        scanf("%d",&f[i][0]);
    initst1(),initst2();
    F1(i,n){
        int l=i+1,r=n;
        if (queryst(l,r)<f[i][0]+m)
            ans[i]=-1;
        else{
            while (l!=r){
                if (queryst(mid,r)>=f[i][0]+m)
                    l=mid;
                else
                    r=mid-1;
            }
            ans[i]=l-i-1;
        }
    }
    F1(i,n-1)
        printf("%d ",ans[i]);
    printf("%d\n",ans[n]);
    return 0;
}

F. Little M's attack plan

听无敌的cyy说是长链剖分,显然是我不会的题目。

代码:待补...

G. Colorful String

标解是回文自动机或回文树,显然也是我不会的题目。

这个题让我们纠结了n久,一开始我的思路是枚举每个位置,后缀数组或者马拉车可以预处理每个位置的最大回文半径,再依次计算。但这样每个位置的复杂度最多是O(n^2)的。后来大佬告诉我一个结论说字符串最多有n个本质不同的回文子串,所以记忆化一下是O(n)的,但又感觉复制字符串会T,就没敢写。随后要自己试一下。

jwp的做法是,预处理每个位置后面第一个出现个字母的位置,这样复杂度是O(26*n)稳过。和M题几乎一样的思路,大佬直接自闭。

代码:

#include<bits/stdc++.h>
#define N 300000
using namespace std;
#define ll long long
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll ans;
int n;
char ch[N+5];
struct pam{
	int cnt,last;
	int a[2*N+5][26],fa[2*N+5],l[2*N+5],size[2*N+5];
	pam(){
	    cnt=1;fa[0]=fa[1]=1;l[1]=-1;
	}
	void extend(int c,int n){
		int p=last;
		while(ch[n-l[p]-1]!=ch[n])p=fa[p];
		if(!a[p][c])
		{
			int now=++cnt,k=fa[p];
			l[now]=l[p]+2;
			while(ch[n-l[k]-1]!=ch[n])k=fa[k];
			fa[now]=a[k][c];a[p][c]=now;
		}
		last=a[p][c];
		size[last]++;
	}
	void solve(){
		for(int i=cnt;i;i--)
		{
			size[fa[i]]+=size[i];
		}
	}
}pam;
int L[2*N+5];
int vis[50],tmp;
inline void DFS(int rt)
{
	for(int i=0;i<26;i++){
		if(vis[i])L[rt]++;
	}
	for(int i=0;i<26;i++){
		if(pam.a[rt][i]){
			vis[i]++;
			DFS(pam.a[rt][i]);
			vis[i]--;
		}
	}
}
int main()
{
	scanf("%s",ch+1);
	n=strlen(ch+1);
	for(int i=1;i<=n;i++)
		pam.extend(ch[i]-'a',i);
	pam.solve();
	DFS(0);
	memset(vis,0,sizeof(vis));tmp=0;DFS(1);
	for(int i=1;i<=pam.cnt;i++){
		ans+=pam.size[i]*L[i];
	}
	cout<<ans;
	return 0;
}

H. function

一看到数学题就激动,终于能用上自己学过的杜教筛了。最后发现不会做。wzk给了一个分块打表的做法。标解是分块,枚举质数的贡献,我其实也想到了。其实这种情况都是自己没做过所以没有信心也没有处理细节的能力,还是多做题吧。这种分块和质数的题目还是挺配的,多留意。

最后,还是得抽空学一下min_25筛或者洲阁筛了。

代码:待补

I. query

让我宕机两个小时的题。其实就是离线处理。我想到了离线枚举所有的倍数对,一共nlogn个,但就是忘了离线处理查询,还是没做过的事,经验太少了。

思路:建单点加减区间求和的线段树,叶节点表示每个位置。插入一对倍数的操作是,将左边位置的点权值+1。首先将所有倍数的两个对应位置存起来(nlogn对倍数),按右端点从小到大排序,再将询问的两个位置存起来,右端点从小到大排序。之后依次处理查询,处理之前先将所有右端点比查询右端点小的倍数对插入(执行+1操作),之后直接输入[l,r]区间和即可。

可以这样理解:对每个询问,当前只插入了[1,r]之间的所有倍数对,倍数对分成三类,一类是两个端点都在[1,l],第二类是左端点在[1,l],右端点在[l,r],第三类是两个端点都在[l,r]。我们这里只算第三类,第三类的每一对左端点都+1,都在[l,r]之间。但一二类的左端点都在[1,l],所以不会被计入。

该问题还可以抽象为:给定n个小区间,给q次询问,每次给一个大区间,问大区间内有几个小区间。就用这种全离线做法。复杂度为O(nlognlog(nlogn))

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)/2)
#define ld long double
#define F first
#define S second
#define ms(x,y) memset(x,y,sizeof(x))
#define F1(i,n) for(int i=1;i<=n;++i)
#define F0(i,n) for(int i=0;i<n;++i)
#define vi vector<int>
#define pii pair<int,int>
#define pb push_back
#define rd(a) scanf("%d",&a)
#define rld(a) scanf("%I64d",&a)
#define rs(a) scanf("%s",a)
const int MAX=2e5,P=1e9+7;
int v[MAX*4];
LL query(LL id,LL l,LL r,LL ql,LL qr){
    if (ql<=l&&r<=qr)
        return v[id];
    LL ans=0;
    if (ql<=mid)
        ans+=query(id*2,l,mid,ql,qr);
    if (mid<qr)
        ans+=query(id*2+1,mid+1,r,ql,qr);
    return ans;
}
void operate(LL id,LL l,LL r,LL pos,LL x){
    if (l==r){
        v[id]+=x;
        return;
    }
    if (pos<=mid)
        operate(id*2,l,mid,pos,x);
    if (mid<pos)
        operate(id*2+1,mid+1,r,pos,x);
    v[id]=v[id*2]+v[id*2+1];
}
int a[MAX],a_rev[MAX],ans[MAX];
struct pi{
    int first,second,third;
    pi(int a=0,int b=0,int c=0){
        third=c;
        if (a>b)
            first=b,second=a;
        else
            first=a,second=b;
    }
};
struct cmp{
    bool operator()(pi x,pi y){
        return x.S>y.S;
    }
};
priority_queue<pi,vector<pi>,cmp> pq,que;
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    F1(i,n)
        scanf("%d",&a[i]),a_rev[a[i]]=i;
    F1(i,n)
        for (int j=i*2;j<=n;j+=i)
            pq.push(pi(a_rev[i],a_rev[j],i));
    int c1,c2;
    F1(i,m)
        scanf("%d%d",&c1,&c2),que.push(pi(c1,c2,i));
    while (!que.empty()){
        int c1=que.top().F,c2=que.top().S,c3=que.top().third;
        que.pop();
        while (!pq.empty()&&pq.top().S<=c2)
            operate(1,1,n,pq.top().F,1),pq.pop();
        if (c1!=c2)
            ans[c3]=query(1,1,n,c1,c2);
        else
            ans[c3]=0;
    }
    F1(i,m)
        printf("%d\n",ans[i]);
    return 0;
}

J. Random Access Iterator

题意:一个人要dfs求一棵树的深度,但枚举每个儿子的时候并不是依次选儿子,而是随机选,问输出正确答案的概率。

树上dp,注意转移即可。

代码:待补...

K.Center

比较简单的计算几何。直接暴力枚举所有点对,算出中心,用哈希或者map存结果,map[pair]=j,表示如果选择pair为中心,则会有j个点不需要额外的点来与他们配对。

所以读入点的时候,直接对每个点坐标+1。每算出一个中心,中心就+2。最后输出n-max即可。

注意特判1个点的情况!!!复杂度为O(n^2)。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)/2)
#define ld long double
#define F first
#define S second
#define ms(x,y) memset(x,y,sizeof(x))
#define F1(i,n) for(int i=1;i<=n;++i)
#define F0(i,n) for(int i=0;i<n;++i)
#define vi vector<int>
#define pii pair<double,double>
#define pb push_back
#define rd(a) scanf("%d",&a)
#define rld(a) scanf("%I64d",&a)
#define rs(a) scanf("%s",a)
const int MAX=1e3+20,P=1e9+7;
int x[MAX],y[MAX];
map<pii,int> mp; //ok
int main(){
    int n,c,mx=0;
    scanf("%d",&n);
    F1(i,n)
        scanf("%d%d",&x[i],&y[i]),++mp[pii(x[i],y[i])];
    F1(i,n)
        for (int j=i+1;j<=n;++j){
            c=(mp[pii((x[i]+x[j])/2.0,(y[i]+y[j])/2.0)]+=2);
            if (c>mx)
                mx=c;
        }
    if (n==1)
        printf("0\n");
    else
        printf("%d\n",n-mx);
    return 0;
}

L. Dice

没读题,听无敌的wzk也没听懂。

代码:不补了。

M. Longest subsequence

问最长子序列,一开始以为是最长子串,最后艾继涛写的,我也没怎么懂。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<stack>
#include<map>
using namespace std;
int n,m;
char s[1000006];
char t[1000006];
int KP[26][1000006];
//int OP[26][1000006]; 
int ans=0;
int STA=0;
int tail=1;
int main()
{
	int i,j,k,l;
	scanf("%d%d",&n,&m);
	scanf("%s",s+1);
	scanf("%s",t+1);
	for(i=n;i>=1;i--){
		for(j=0;j<26;j++){
			if((s[i]-'a')>j)KP[j][i]=i;
			else KP[j][i]=KP[j][i+1];
		}
	}
	if(KP[t[1]-'a'][1]!=0)ans=max(ans,n-KP[t[1]-'a'][1]+1);
	for(i=1;i<=n;i++){
		if(t[tail]==s[i]){
			tail++;
			if(tail<=m)if(KP[t[tail]-'a'][i+1]!=0)ans=max(ans,n-KP[t[tail]-'a'][i+1]+tail);
			if(tail==(m+1)&&i!=n){
				ans=max(ans,n-i-1+tail);
				break;
			}
		}
	}
	if(ans==0)ans=-1;
	printf("%d\n",ans);
	return 0;
}

总结

今天打的一坨屎。qz爷队都过了10个,直追sl队,我们还是停留在7个题,关键的G和L都没做出来。其实想的很接近了,就差最后一步,很可惜。如果之前见过类似的,比如说I如果见过离线查询,我也就会做了。

希望这是最后一次倒二。

posted on 2019-09-08 01:41  diorvh  阅读(229)  评论(0)    收藏  举报