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;
}