2019徐州网络赛A,B,C,D,E,G,I,K,M

 

A 中国剩余定理,I 树状数组解决偏序问题,G回文树

A. Who is better?

题打表发现是一个斐波拉契数的博弈,是斐波拉契数的就是先手必胜。

题目链接:https://nanti.jisuanke.com/t/41383

这里出题人强行套了一个中国剩余定理。。。

#include 

using namespace std;

#define ll long long

const int maxn=2000;
ll n,m;
ll ai[maxn],bi[maxn];
ll sum[maxn];
ll N=1e16;
ll mul(ll a,ll b,ll mod){
    ll res=0;
    while(b>0){
        if(b&1) res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}

ll exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){x=1;y=0;return a;}
    ll gcd=exgcd(b,a%b,x,y);
    ll tp=x;
    x=y; y=tp-a/b*y;
    return gcd;
}

ll excrt(){
    ll x,y,k;
    ll M=bi[1],ans=ai[1];
    for(int i=2;i<=n;i++){
        ll a=M,b=bi[i],c=(ai[i]-ans%b+b)%b;
        ll gcd=exgcd(a,b,x,y),bg=b/gcd;
        if(c%gcd!=0) return -1;

        x=x*c/gcd%bg;
        ans+=x*M;
        M*=bg;
        ans=(ans%M+M)%M;
    }
    return (ans%M+M)%M;
}

int main()
{
    int len=73;
    sum[0]=1;
    sum[1]=1;
    for(int i=2;i<=len;++i)sum[i]=sum[i-1]+sum[i-2];
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%lld%lld",&bi[i],&ai[i]);


    ll ans=excrt();
    //ans=13;
    //printf("ans:%lld\n",ans);
    if(ans<=1||ans>N){
        printf("Tankernb!");
        return 0;
    }
    bool flag=0;
    for(int i=1;i<=len;++i)
    {
        if(sum[i]==ans){
            printf("Lbnb!");
            return 0;
        }
    }
    printf("Zgxnb!");
    return 0;
}

B. so easy

保存每个输入的val和val+1 离散化一下,线段树维护num  当前区间有多少个可用的数。简单题

#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e6+10;
struct node
{
    int ty,val;
}a[2*N];
int X[2*N];
int sum[8*N];
int len;
int getid(int x)
{
    return lower_bound(X+1,X+1+len,x)-X;
}
void build(int id,int l,int r)
{
    sum[id]=r-l+1;
    if(l==r) return ;
    int mid=l+r>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
}
void up(int id,int l,int r,int pos)
{
    sum[id]--;
    if(l==r) return ;
    int mid=l+r>>1;
    if(pos<=mid) up(id<<1,l,mid,pos);
    else up(id<<1|1,mid+1,r,pos);
}
bool flag;
int ans;
void qu(int id,int l,int r,int ql,int qr)
{
    if(l==r){
        if(sum[id])
        {
            flag=1;
            ans=X[l];
        }
        return ;
    }
    int mid=l+r>>1;
    if(!flag&&ql<=mid&&sum[id<<1]) qu(id<<1,l,mid,ql,qr);
    if(!flag&&sum[id<<1|1]) qu(id<<1|1,mid+1,r,ql,qr);
}
int main()
{
    int n,q;
    cin>>n>>q;
    rep(i,1,q)
    {
        scanf("%d%d",&a[i].ty,&a[i].val);
        X[++len]=a[i].val;
        X[++len]=a[i].val+1;
    }
    sort(X+1,X+1+len);
    len=unique(X+1,X+1+len)-X-1;
    build(1,1,len);
    rep(i,1,q)
    {
        if(a[i].ty==1) up(1,1,len,getid(a[i].val));
        else{
            flag=0;
            ans=-1;
            qu(1,1,len,getid(a[i].val),len);
            printf("%d\n",ans);
        }
    }
}

C. Buy Watermelon 签到题

 

#include 

using namespace std;

#define ll long long

int w;

int main(){
	scanf("%d",&w);
	if(w%2==0&&w!=2){
		printf("YES\n");
	}else printf("NO\n");
}

D. Carneginon

hash搞一搞就完事了。kmp也行,各种做法。

#include
#define ll long long
using namespace std;
const int N = 3e5 + 10;
//base[i]进制
ll h[3][N],h1[3][N];
ll base[3]={43,47,41};
ll mod[3] = {1000000007,998244353,1000000009};
ll f[3][N];
inline int getid(char c) {
    return c-'a'+1;
}
char s[N],t[N];
void init()
{
    f[1][0]=f[0][0]=f[2][0]=1;
    for(int i=1;imp[3];
int main()
{
    init();
    scanf("%s",s+1);
    int n=strlen(s+1);
    for(int i=1;i<=n;++i)
    for(int j=0;j<3;++j)
        h[j][i]=(h[j][i-1]*base[j]%mod[j]+getid(s[i]))%mod[j];
    int q;
    scanf("%d",&q);
    while(q--)
    {
        scanf("%s",t+1);
        int m=strlen(t+1);
        for(int i=1;i<=m;++i)
        {
            for(int j=0;j<3;++j)
            h1[j][i]=(h1[j][i-1]*base[j]%mod[j]+getid(t[i]))%mod[j];
        }
        if(n>m)
        {
            bool f=0;
            for(int i=1;i+m-1<=n&&!f;++i)
            {
                bool flag=1;
                for(int j=1;j<3;++j)
                {
                    if(h1[j][m]==getvalue(i,i+m-1,j)) continue;
                    else flag=0;
                }
                if(flag) f=1;
            }
            if(f) printf("my child!\n");
            else printf("oh, child!\n");

        }else if(n

E. XKC's basketball team

这题自己想了一个假的思路,写了半天,然后队友A了的。做法:建立一个权值线段树,给每个a[i]值赋一个下标i,有相同的取最大的下标。维护树上的最大坐标即可。区间查询最大值。

#include 

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=5e5+7;

int t[N*4];

ll n,m,a[N];
ll Ans[N];

int len=0;
ll X[N*2];

int getid(ll x) {return lower_bound(X+1,X+1+len,x)-X;}

void updata(int rt,int l,int r,int pos,int x){
	if(l==r){
		t[rt]=x;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) updata(rt<<1,l,mid,pos,x);
	else updata(rt<<1|1,mid+1,r,pos,x);
	t[rt]=max(t[rt<<1],t[rt<<1|1]);
}

int query(int rt,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr){
		return t[rt];
	}
	int mid=(l+r)>>1,res=0;
	if(ql<=mid) res=max(res,query(rt<<1,l,mid,ql,qr));
	if(qr>mid) res=max(res,query(rt<<1|1,mid+1,r,ql,qr));
	return res;
}

int main(){
	n=input(),m=input();
	for(int i=1;i<=n;i++){
		a[i]=input();
		X[++len]=a[i];
	}

	sort(X+1,X+1+len);
	len=unique(X+1,X+1+len)-X-1;

	for(int i=1;i<=n;i++){
		updata(1,1,len,getid(a[i]),i);
	}

	for(int i=1;i<=n;i++){
		if(a[i]+m

I query

补题来自:https://blog.csdn.net/HNUCSEE_LJK/article/details/100638270

题面:

2019徐州网络赛A,B,C,D,E,G,I,K,M_第1张图片

由于给的是一个排列,所以我可以nlogn的时间复杂求出所有符合题目条件的(i,j)出来,如何实现,看代码。。。

那么就成功的将问题转换成了,问区间(l,r)内有多少个合法的(i,j)..这里就是一个二维偏序问题,

常见的二维偏序 形式:每个单位 (a, b) 找到有多少 (x, y) 满足 a > x 且 b < y。 

这里就是每个单位(l,r),有多少(i,j)  满足l

这里的二维偏序怎么处理呢?

我学的那篇题解讲的不错,就是字多,看起来难受,细心的看,还是很好理解的。

 

首先,读入数据时,我们将每个值对应的位置记录下来。然后,对于 [1, n] 的每个值 p[i],找出序列中与 p[i] 成倍数关系的值 p[j](p[j] = k*p[i], k = 2, 3, 4...),得到它的位置 j,并与 i 构成一个区间 [i, j],可以称之为贡献区间。若 [i, j] 在查询的范围 [l, r] 之内,那么就能为答案贡献 +1。当我们找到所有这样的区间时,将这些贡献区间与询问区间混合,并进行排序,

 

排序的规则是按照区间的左端点从大到小排列,当贡献区间与询问区间的左端点相同时,则贡献区间在前。然后依次处理每个区间。

         枚举区间,当遇到贡献区间时,贡献区间的右端点在树状数组上加1,当遇到询问区间时,答案加上   树状数组 1到询问区间右端点的和。最后,由于询问是离线处理的,所以要根据询问的顺序依次输出答案。

 

至于为什么,因为区间先按左端点从大到小枚举。遇到的询问区间,询问区间的左端点一定包含了  之前出现的贡献区间的左端点。那么只需要查询有多少个右端点比询问区间的右端点小的就可以了。

代码:

#include
using namespace std;
const int N=1e5+10;
int p[N],pos[N],a[N];
int n,m;
int sum[N],ans[N];
int low(int x)
{
    return x&(-x);
}
struct node
{
    int l,r,id,f;
    bool operator <(const node &o)const
    {
        if(o.l!=l) return l>o.l;
        return f>o.f;
    }
}q[N*40];
void up(int x)
{
    for(;x<=n;x+=low(x))
        sum[x]++;
}
int qu(int x)
{
    int res=0;
    for(;x;x-=low(x)) res+=sum[x];
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) {
        scanf("%d",&a[i]);
        pos[a[i]]=i;
    }
    int len=0;
    for(int i=1;i<=m;++i)
    {
        int l,r;scanf("%d%d",&l,&r);
        q[++len]={l,r,i,0};
    }
    for(int i=1;i<=n;++i)
    for(int j=2;i*j<=n;++j)
        q[++len]={min(pos[i],pos[i*j]),max(pos[i],pos[i*j]),0,1};
    /*
    for(int i=1;i<=n;++i)
    for(int j=2*a[i];j<=n;j+=a[i])
    q[++len]={min(i,pos[j]),max(i,pos[j]),0,1};
*/
    sort(q+1,q+1+len);
    for(int i=1;i<=len;++i)
    {
        if(q[i].f) up(q[i].r);
        else ans[q[i].id]=qu(q[i].r);
    }
    for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
    return 0;
}


 

K. Center

枚举两个点的中点作为轴对称的点就可以了。队友A的

#include 

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

#define PII pair
#define x first
#define y second
#define mp make_pair

const int N=1007;

int n;
PII pt[N];
map  mpp;

vector mid;

int main(){
	n=input();

	for(int i=1;i<=n;i++){
		pt[i].x=2ll*input(),pt[i].y=2ll*input();
	}

	ll mx=0;

	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			PII tmp=mp((pt[i].x+pt[j].x)/2,(pt[i].y+pt[j].y)/2);
			mpp[tmp]++;
			if(mpp[tmp]>mx) mx=mpp[tmp];
		}
	}

	// cout<

M Longest subsequence

思路很容易想到,枚举t串,在s串中出现大于t[i]的s[j]那么s的后面都要取。

那么做法就是枚举t串,枚举比当前t[i]大的字符,在s串中序列自动机找一找,若有,更新一下答案。

接着判断是否有与t[i]相等的,有 继续枚举t串,没有 break掉。其实很水,思路当时也出了,自己也会序列自动机 ,可为什么这个题当时没A。。。。

#include
using namespace std;
const int N=1e6+10;
char s[N],t[N];
int nxt[N][27];
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    scanf("%s%s",s+1,t+1);
    for(int i=n;i>=1;i--)
	{
		for(int j=0;j<=25;j++) nxt[i-1][j]=nxt[i][j];
		nxt[i-1][s[i]-'a']=i;
	}
	int ans=-1;
	int cur=0,pre=0;
    for(int i=1;i<=m;++i)
    {
        for(int j=t[i]-'a'+1;j<=25;++j)
        {
            int ne=nxt[pre][j];
            if(ne==0) continue;//没有找到
            ans=max(ans,cur+n-ne+1);
        }
        int ne=nxt[pre][t[i]-'a'];
        if(ne==0) break;
        cur++;pre=ne;
        if(i==m&&n>m) ans=max(ans,cur+n-ne);
    }
    printf("%d\n",ans);
}

 

你可能感兴趣的:(网络赛题解,数学--中国剩余定理,数据结构---树状数组&RMQ)