North American Invitational Programming Contest 2018 (NAIPC2018)(solve5/11)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/passer__/article/details/82144000

claris大佬题解:https://www.cnblogs.com/clrs97/p/8730429.html

题目链接:https://nanti.jisuanke.com/?kw=The%20North%20American%20Invitational%20Programming%20Contest%202018

C题:

题意:给你一个由01组成的串表示灯泡的明亮,1是亮0是灭,问你多久可以让所有的灯都亮,对于你点开的某个灯有后效性,也就是说i秒按了j的按钮,那么i+1自动改变j+1的状态,对于同一个点按按钮和后效性的灯亮可以抵消。

思想:对于一个01串,最终变成全部1组成的串,那么需要将000区间插进入到原始的01串,然后考虑枚举长度和起点,然后不断的转移就行了,最后让原来的那个01串0的部分^==0就是最后的结果

#include<bits/stdc++.h>
using namespace std;
int dp[50][1<<17];
char s[50]; 
int main()
{
    scanf("%s",s);
    int len=strlen(s);
    int ans=0;
    for(int i=0;i<len;i++)//将所有0抠出来 等0变成1即使结果 
        if(s[i]=='0')
            ans=ans^(1<<i);
    dp[0][ans]=1;//初始
    int start=0; 
    while(!dp[start][0])
    {
        for(int i=0;i<1<<len;i++)//start++ 更新状态 
            dp[start+1][i]=dp[start][i];
        for(int i=0;i<len;i++)//枚举起点 
        {
            int flag=0;
            for(int j=0;j<start+1 && i+j<len;j++)//枚举长度 
                flag|=(1<<(i+j));
            for(int j=0;j<1<<len;j++)
                if(dp[start][j]) 
                    dp[start+1][j^flag]=1;
        }
        start++;
    }
    printf("%d\n",start);
    return 0;
}

D题

题意:就是让你将n-m个数插入到m个数中,让字典序最小,然后不破坏m个数的顺序。

思想:模拟即可

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int vis[maxn];
int a[maxn];//没放的  
int b[maxn];//放的 
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int k=0;
    int kk=0;
    for(int i=0;i<m;i++)
    {
        int temp;
        scanf("%d",&temp);
        vis[temp]=1;
        b[kk++]=temp;
    }
    for(int i=1;i<=n;i++)
    {
        if(vis[i]==0)
            a[k++]=i;
    }
    int i=0,j=0;
    while(i<k && j<kk)
    {
        if(a[i]<b[j])
            printf("%d\n",a[i++]);
        else
            printf("%d\n",b[j++]);
    } 
    while(i<k)
    {
        printf("%d\n",a[i++]);
    } 
    while(j<kk)
    {
        printf("%d\n",b[j++]);
    }
    return 0;
}

E题

题意:给你n个字符串,然后从其中随便选出来m个全排列,然后再给你子串,问你是在这些排列串的第几个。

思想:字典树建树+树状数组维护有多少比较小的,然后考虑树状数组维护有多少个比他小的。

做法①:康拓展开式求解。康拓的话百度下就晓得了。

做法②:组合数学且化简公式。借鉴一个老哥的证明:https://blog.csdn.net/qq_40772692/article/details/81738156

康托展开代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const ll mod = 1e9+7;
struct node{
    char s;
    int next;
    int flag;
}no[maxn]; 
string ss[maxn];
int head[maxn];
int bitree[maxn];
ll fac[maxn];
vector<int>V;
int cnt,n,m;
void insert(string s,int id)
{
    int u=0;
    int len=s.length();
    for(int i=0;i<len;i++)
    {
        int flag=1; 
        for(int j=head[u];j!=-1;j=no[j].next)
        {
            if(no[j].s==s[i])
            {
                flag=0;
                u=j;
                break;
            }
        }
        if(flag)
        {
            no[++cnt]=node{s[i],head[u],0};
            head[u]=cnt;
            u=cnt;
        } 
    }
    no[u].flag=id;//标记单词结尾 
}
void add(int id,int valu)
{
    for(;id<=n;id+=id&(-id))
    {
        bitree[id]+=valu; 
    }
} 
int check(int id)
{
    int ans=0; 
    for(;id>0;id-=id&(-id))
        ans+=bitree[id];
    return ans;
}
int main()
{
    memset(head,-1,sizeof(head));
    string s;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>s;
        ss[i]=s;
    }
    sort(ss+1,ss+n+1);
    for(int i=1;i<=n;i++)
        insert(ss[i],i);
    for(int i=0;i<=n-m;i++)
        fac[i]=1;
    for(int i=n-m+1;i<=maxn-1;i++)
        fac[i]=fac[i-1]*i%mod;
    cin>>s;
    int u=0;
    int len=s.length();
    for(int i=0;i<len;i++)
    {
        for(int j=head[u];j!=-1;j=no[j].next)
        {
            if(no[j].s==s[i])
            {
                u=j;
                break;
            }
        }
        if(no[u].flag)//记录是那些单词结尾
        {
            V.push_back(no[u].flag);
            u=0;
        }
    }   
    for(int i=1;i<=n;i++)
    {
        add(i,1);
    } 
    ll ans=0;
    for(int i=0;i<V.size();i++)
    {
        add(V[i],-1);
        int temp=check(V[i]);
        ans+=fac[n-i-1]*temp%mod;
        ans=ans%mod; 
    }
    printf("%lld\n",(ans+1)%mod);
    return 0;
}

组合数学代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const ll mod = 1e9+7;
struct node{
    char s;
    int next;
    int flag;
}no[maxn]; 
string ss[maxn];
int head[maxn];
int bitree[maxn];
ll fac[maxn];
ll inv[maxn];
vector<int>V;
int cnt,n,m;
void insert(string s,int id)
{
    int u=0;
    int len=s.length();
    for(int i=0;i<len;i++)
    {
        int flag=1; 
        for(int j=head[u];j!=-1;j=no[j].next)
        {
            if(no[j].s==s[i])
            {
                flag=0;
                u=j;
                break;
            }
        }
        if(flag)
        {
            no[++cnt]=node{s[i],head[u],0};
            head[u]=cnt;
            u=cnt;
        } 
    }
    no[u].flag=id;//标记单词结尾 
}
void add(int id,int valu)
{
    for(;id<=n;id+=id&(-id))
    {
        bitree[id]+=valu; 
    }
} 
int check(int id)
{
    int ans=0; 
    for(;id>0;id-=id&(-id))
        ans+=bitree[id];
    return ans;
}
ll A(int n,int m)
{
    return n<m?0:1LL*fac[n]*inv[n-m]%mod;
}
int main()
{
    memset(head,-1,sizeof(head));
    string s;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>s;
        ss[i]=s;
    }
    sort(ss+1,ss+1+n);
    for(int i=1;i<=n;i++)
        insert(ss[i],i);
    fac[0]=1;
    for(int i=1;i<=n;i++)
        fac[i]=1LL*fac[i-1]*i%mod;
    inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)
        inv[i]=1LL*(mod-inv[mod%i])*(mod/i)%mod;
    for(int i=2;i<=n;i++)
        inv[i]=1LL*inv[i-1]*inv[i]%mod;
    cin>>s;
    int u=0;
    int len=s.length();
    for(int i=0;i<len;i++)
    {
        for(int j=head[u];j!=-1;j=no[j].next)
        {
            if(no[j].s==s[i])
            {
                u=j;
                break;
            }
        }
        if(no[u].flag)//记录是那些单词结尾
        {
            V.push_back(no[u].flag);
            u=0;
        }
    }   
    for(int i=1;i<=n;i++)
    {
        add(i,1);
    } 
    ll ans=1;
    for(int i=0;i<V.size();i++)
    {
        add(V[i],-1);
        int temp=check(V[i]);
        ans=(ans+(A(n-i-1,m-i-1)*temp)%mod)%mod;
        ans=ans%mod; 
    }
    printf("%lld\n",ans);
    return 0;
}

H题:

题意:给你一个行规则一个列规则,行中1的个数是奇数就是1偶数就是0,列同理,问你是否可以构造出来一个01矩阵,1尽量多的情况下。

思想:找那些行内1的个数是奇数的,但是列确实偶数(说明多一个),行是偶数,列是奇数的,将原始的初始化为1,然后自己手写几组发现,当不匹配的行和列的数量是奇数的时候,你怎么凑也不行,然后查询凑的行数和列数,小的往大的凑,起码凑成对数,因为字典序最小,sort下,匹配就行了。

#include<bits/stdc++.h>
using namespace std;
char str[2][60];
vector<int>V[2];
int Map[60][60];
int main()
{
    scanf("%s",str[0]);
    scanf("%s",str[1]);
    int len=strlen(str[0]);
    int Len=strlen(str[1]);
    for(int i=0;i<len;i++)
        if((str[0][i]-'0')%2!=Len%2)//寻找那些不一的行 
            V[0].push_back(i+1);
    for(int i=0;i<Len;i++)
        if((str[1][i]-'0')%2!=len%2)//列 
            V[1].push_back(i+1); 
    for(int i=1;i<=len;i++)//全部初始化为1 然后贪心操作 
        for(int j=1;j<=Len;j++)
            Map[i][j]=1;
    if((V[0].size()+V[1].size())%2==1)//不匹配的行和列和奇数构造不出来 
    {
        printf("-1\n");
        return 0;
    }
    while(V[0].size()<V[1].size())
        V[0].push_back(1);
    while(V[1].size()<V[0].size())
        V[1].push_back(1);
    sort(V[0].begin(),V[0].end());
    sort(V[1].begin(),V[1].end());
    for(int i=0;i<V[0].size();i++)
        Map[V[0][i]][V[1][i]]=0;
    for(int i=1;i<=len;i++)
    {
        for(int j=1;j<=Len;j++)
            printf("%d",Map[i][j]);
        printf("\n");
    }
    return 0;
} 

K 题:

题意:给你n个点的(x,y),然后给你m个区间,问你区间最多去掉1个点之后,可以用一个多大的正方形圈起来,输出边长。

思路:感觉线段树维护去掉x,y的最大最小值然后比较求一个最小的即可,然后疯狂的T到我怀疑我是不是写错了,后来问了问说可能是线段树卡成n^2,然后就RMQ求区间最值,稳定nlogn.

先ST表,然后通过不同的t判断是求哪一个,然后输出最小值即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int inf = 2e9;
struct node{
	int first;
	int second;
	bool operator < (const node &b) const
	{
		if(first!=b.first)
			return first<b.first;
		return second<b.second;
	}
}maxx[maxn][20],minx[maxn][20],maxy[maxn][20],miny[maxn][20];
int n,m,ans=0;
void ST(int n)//ST表 
{
    for (int j=1;(1<<j)<=n;j++)
    {
    	int temp=1<<j; 
        for (int i=1;i+temp-1<=n;i++)
        {
            minx[i][j]=min(minx[i][j-1],minx[i+(1<<(j-1))][j-1]);
            miny[i][j]=min(miny[i][j-1],miny[i+(1<<(j-1))][j-1]);
            maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1]);
            maxy[i][j]=max(maxy[i][j-1],maxy[i+(1<<(j-1))][j-1]);
        }
    }
}
node RMQ(int l,int r,int t)
{
    if(l>r)
    {
        if(t==1||t==3)
			return node{inf,0};
        return node{-inf,0};
    }
    int k=0;
    while((1<<(k+1))<=r-l+1) 
		k++;
    if (t==1) 
		return min(minx[l][k],minx[r-(1<<k)+1][k]);
    else if (t==2)
		return max(maxx[l][k],maxx[r-(1<<k)+1][k]);
    else if (t==3)
		return min(miny[l][k],miny[r-(1<<k)+1][k]);
    else if (t==4)
		return max(maxy[l][k],maxy[r-(1<<k)+1][k]);
}
void check(int x,int y,int pos)
{
    int dx=max(RMQ(x,pos-1,2).first,RMQ(pos+1,y,2).first)-min(RMQ(x,pos-1,1).first,RMQ(pos+1,y,1).first);
    int dy=max(RMQ(x,pos-1,4).first,RMQ(pos+1,y,4).first)-min(RMQ(x,pos-1,3).first,RMQ(pos+1,y,3).first);
    ans=min(ans,max(dx,dy));
}
int main()
{
	scanf("%d",&n);
	scanf("%d",&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&minx[i][0].first,&miny[i][0].first);
		minx[i][0].second=i;
		miny[i][0].second=i;
		maxx[i][0]=minx[i][0];
		maxy[i][0]=miny[i][0];
	}
	ST(n);
	int x,y;
	while(m--)
	{
		scanf("%d%d",&x,&y);
		ans=inf;
		check(x,y,RMQ(x,y,1).second);
        check(x,y,RMQ(x,y,2).second);
        check(x,y,RMQ(x,y,3).second);
        check(x,y,RMQ(x,y,4).second);
        printf("%d\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/passer__/article/details/82144000
今日推荐