North American Invitational Programming Contest 2018
题目链接:https://nanti.jisuanke.com/?kw=The%20North%20American%20Invitational%20Programming%20Contest%202018
本博客参考了(抄了)巨佬Claris的博客 https://www.cnblogs.com/clrs97/p/8730429.html,但是因为Claris太巨了,每道题就几句话完事了,我结合自己的理解补充了一下,部分由于自己的菜,暂时还不会,留着以后补。
A-Cut it out
本题是结合一部分计算几何的DP,等我学会计算几何(xia bei zi)再说吧··
B-Double Clique
题意
给出一个n个结点m条边的无向图G,点集是V,边集是E。G'是G的补集。一个图的补集中的点和原图中的点相同,但是所有原图中没有的边补集中都有,原图中有的边补集中没有。 Clique是点的子集,且任意两点之间都有一条边,一个结点的子集和S被称为Double Clique当且仅当,S在图G中是clique,且V-S在G’中是clique。注意,一个空的结点集合也被认为是clique。给出一个图,计算double clique在图中的数量。
分析
首先给出一个结论:一个方案合法当且仅当 团点数*(团点数-1)+独立集度数和=团度数和 。
为什么呢,我们来想一下,前i个点是原图中能构成团的点,i+1到n个点是补图中能构成团的点(独立集),前i个点每个点至少都和团中其他所有点都有边相连,这些度数为i*(i-1),还可能有一些边连向独立集中一些点的边,而独立集内部是没有互相连得边的,所以团中连入独立集的边等于独立集中度数的和。
然后我们通过上面这个结论找出一个合法的方案以后,然后要么从团中拿出一个点放到独立集中,要么从独立集中拿出一个点放进团中,要么是从两边各拿出一个点交换。前面两种很好理解,因为如果团中两个点放到独立集中,独立集就不是独立集了(团中点相互连着边)。关键是最后的交换怎么交换。我们想团和独立集中的点交换的条件是什么?i是团内点的数量,n-i+1是独立集中点的数量。如果团内度数最少的点的度数是i-1,那么这个点可以和独立集中度数为i-1的点进行交换。如果团内度数最少的点的度数为i,那么就说明这个点有一条边是连向独立集,那么这些点可以跟独立集内度数为i的点交换。如果团内度数最少的点度数大于i,那么久说明最少的这个点也有两条以上的边连向独立集,这时候没有可以交换的点,因为此时团内任何一个点和独立集交换,独立集内都有边了。
代码可以去看Claris老师的呦~
C-Flashing Fluorescents
题意
你有n个灯,每个灯都有一个按钮,排成一排。按下一个开关将会改变灯泡状态。灯泡改变需要一秒钟(时间步长),你可以在任何一个时间按下一个按钮,但是它会在下一秒起作用。在每一秒前,你都可以选择并按下一个按钮。灯的开关具有传递性。 即第i秒前如果按下开关j,那么i秒灯泡j状态发生变化,i+1秒,j+1灯泡发生变化,i+2秒,j+2灯泡发生变化。可以相互抵消。给出初始状态,问最少需要多少秒可以打开所有的灯 最多有16的灯泡和开关。
分析
个人感觉这个题特别巧妙。f[now][S]是在now步内,能否走到S状态。我们可以反着想,当第now秒的时候,最长的那一段走了now长(从第一秒开始走的状态),然后第二秒开始走的状态最长走了now-1秒。然后理解了按照那个递推式暴力枚举转移就好惹。
D-D. Missing Gnomes
签到题
E- Prefix Free Code
题意
这个题就是拿trie+树状数组就可以,并不是很难。因为题目保证给出的单词中没有哪个是另一个的前缀, 所以我们可以很轻松的用trie给单词排序并且可以通过trie来找出单词,树状数组维护的是是出现过的小于的单词有多少个。
哈哈这个题有代码了,但是好几天前写的了所以解析写的不够详细有疑问的可以加QQ(好了好了我知道我博客没人看)
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 6 using namespace std; 7 typedef long long LL; 8 const int maxn=1000000+20; 9 const int mod=1e9+7; 10 int n,k; 11 LL A[maxn]; 12 13 int ch[maxn][26],val[maxn],sz; 14 void insert(char *s,int v){ 15 int len=strlen(s),u=0; 16 for(int i=0;i<len;i++){ 17 int c=s[i]-'a'; 18 if(!ch[u][c]){ 19 ch[u][c]=sz; 20 memset(ch[sz],0,sizeof(ch[sz])); 21 val[sz++]=0; 22 } 23 u=ch[u][c]; 24 } 25 val[u]=v; 26 } 27 int rak[maxn],hea[maxn],num; 28 void cal(int u){ 29 if(val[u]){ 30 num++; 31 rak[val[u]]=num; 32 hea[num]=val[u]; 33 return ; 34 } 35 for(int i=0;i<26;i++){ 36 if(ch[u][i]) 37 cal(ch[u][i]); 38 } 39 } 40 int sumv[maxn]; 41 int lowbit(int x){ 42 return x&(-x); 43 } 44 void add(int p,int v){ 45 for(int i=p;i<=n;i+=lowbit(i)){ 46 sumv[i]+=v; 47 } 48 } 49 int query(int p){ 50 int res=0; 51 for(int i=p;i>0;i-=lowbit(i)){ 52 res+=sumv[i]; 53 } 54 return res; 55 } 56 int check(char *s){ 57 int u=0;int len=strlen(s); 58 for(int i=0;i<len;i++){ 59 int c=s[i]-'a'; 60 if(!ch[u][c]) 61 return 0; 62 u=ch[u][c]; 63 } 64 return val[u]; 65 } 66 char s[maxn]; 67 int main(){ 68 scanf("%d%d",&n,&k); 69 A[0]=1;A[1]=n-k+1; 70 for(int i=2;i<=k;i++){ 71 A[i]=A[i-1]*(n+i-k)%mod; 72 } 73 74 sz=1; 75 for(int i=1;i<=n;i++){ 76 scanf("%s",s); 77 insert(s,i); 78 } 79 num=0; 80 cal(0); 81 int u=0; 82 scanf("%s",s); 83 LL ans=0; 84 int len=strlen(s); 85 int num=0; 86 for(int i=0;i<len;i++){ 87 int c=s[i]-'a'; 88 u=ch[u][c]; 89 if(val[u]){ 90 int id=rak[val[u]]; 91 // printf("%d %d %d %d %lld\n",i,val[u],id,query(id-1),A(n-num-1,k-num-1)); 92 ans=(ans+(((id-1-query(id-1))%mod)*A[k-num-1])%mod)%mod; 93 // printf("%lld\n",ans); 94 add(id,1); 95 u=0; 96 num++; 97 } 98 } 99 printf("%lld\n",ans+1); 100 return 0; 101 }
F-Probe Droids
不会做···
G-G. Rainbow Graph
题意
Roy和Biv有一个拥有n个结点的无向图,每条边有一个正整数权值和颜色。有三种颜色,红色,绿色,蓝色。可能有重边和自环。 Roy和Biv想要选出k条边,然后删除掉其他的边,且图还是联通的。然而,Roy看不见红色,Biv看不见蓝色,因此,他们必须选择边按照下面的规则:蓝色和绿色的边足够连接所有的点,红色和绿色的边足够连接所有的点。k条边满足条件的最小的。n,m<=100。
分析
个人感觉这个题炒鸡巧妙啊!!!
所以说连通的定义为:只用红色和绿色的边可以连通,且只用蓝色绿色的边也可以连通。Claris是用拟阵解释的,然鹅我不会,我只能用通俗点的方式来解释。定义条件一(M1)为只用红色绿色就能连通,条件二(M2)为只用蓝色绿色就能连通。建立有向图,原图每条边作为一个点,并添加源汇S和T。对于上一个k的一组最优解E中的某条边x,如果去掉它后仍然满足M1,则由S向x连边;若去掉它后仍然满足M2,则由x向T连边。对于E中某条边x和不在E中的某条边y,若将x换成y后满足M2,则由x向y连边;若满足M1,则由y向x连边。 用SPFA求出S到T的最短路,就能得到边数恰好减1的最优解。神奇吧??
下面说一下我的理解。如果去掉边i,条件一仍然满足,则S->i连边,若条件二仍然满足,则i->T连边。这个很好理解。如果i是集合内的边,j是集合外的边,如果交换i和j,条件2满足,则i向j连边。如果条件1满足,则j向i连边。 为什么要这样,因为现在是要找比原来集合少一条边的边集合,所以如果要替换,必须做到二换一。
时间复杂度是O(n^4)
H-Recovery
贪心的构造
I- Red Black Tree
题意
有一个n个节点的有根树,编号由1到n,有m个节点被涂成红色,剩下的涂成黑色。 你要选出一些节点集合来,使得集合内没有一个节点是另一个集合内节点的祖先节点。 另外,你要保证你的集合内恰好有k个红色的节点。有m个红色结点是红色的,对K=0,1,。。,m分别找出有多少符合条件的集合。
分析
比较裸的树形DP。设f[i][j]为以i为根结点的子树,选出j个红色结点的方案数。注意转移的时候。u结点有m个儿子结点,将其从左到右编号为1,2,3,...,m。设f[i][x]为1到i个结点1到i结点,选出有x个红色结点的方案数。f[i][x]=f[i-1][x-j]*f[i][j];
1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 #include <vector> 6 7 using namespace std; 8 typedef long long LL; 9 const int maxn=200000+100; 10 const int mod=1e9+7; 11 const int maxm=1000+5; 12 vector<int>G[maxn]; 13 int n,m; 14 int f[maxn][maxm],h[maxm],Size[maxn],red[maxn]; 15 void dfs(int u){ 16 f[u][0]=1; 17 for(int i=0;i<G[u].size();i++){ 18 int v=G[u][i]; 19 dfs(v); 20 for(int j=0;j<=Size[u]+Size[v]&&j<=m;j++)h[j]=0; 21 for(int j=0;j<=Size[u]&&j<=m;j++){ 22 for(int k=0;k<=Size[v]&&j+k<=m;k++){ 23 h[j+k]=((LL)f[u][j]*f[v][k]+h[j+k])%mod; 24 } 25 } 26 for(int j=0;j<=Size[u]+Size[v]&&j<=m;j++) 27 f[u][j]=h[j]; 28 Size[u]+=Size[v]; 29 } 30 Size[u]+=red[u]; 31 f[u][red[u]]=(f[u][red[u]]+1)%mod; 32 } 33 int main(){ 34 while(scanf("%d%d",&n,&m)!=EOF){ 35 memset(red,0,sizeof(red)); 36 memset(f,0,sizeof(f)); 37 for(int i=0;i<=n;i++)G[i].clear(); 38 39 for(int i=2;i<=n;i++){ 40 int a; 41 scanf("%d",&a); 42 G[a].push_back(i); 43 } 44 for(int i=1;i<=m;i++){ 45 int a; 46 scanf("%d",&a); 47 red[a]=1; 48 } 49 dfs(1); 50 for(int i=0;i<=m;i++) 51 printf("%d\n",f[1][i]); 52 } 53 return 0; 54 }
J-Winter Festival
仍然不会做··我太菜了
K- Zoning Houses
题意
给出n个点的坐标和q个询问。每个询问给出一段区间[l,r],找出一个最小的正方形使得包含区间内所有的点。可以忽略区间内的一个点
分析
如果不考虑忽略一个点的话每次询问只要找出区间内点的最大最小横纵坐标就可以。忽略一个点一定优于或等于不忽略,所以直接考虑忽略哪个点。需要考虑的最多只有四个点,横坐标最大,横坐标最小,纵坐标最大,纵坐标最小。所以我需要查询出横坐标最大的最小的,次大的,次小的。 纵坐标同理。次大的 如果查询区间是[l,r]但是其中x是区间最大值,那么就再分别查询[l,x-1],[x+1,r]的区间最大值,来确定。
claris这个线段树真的是短小精悍,实在忍不住了,默默的贴在这
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 typedef pair<int,int>P; 5 const int N=100010,M=262150,inf=1000000010; 6 int n,m,i,x,y,ans; 7 P xmi[M],xma[M],ymi[M],yma[M]; 8 void build(int x,int a,int b){ 9 if(a==b){ 10 scanf("%d%d",&xmi[x].first,&ymi[x].first); 11 xmi[x].second=ymi[x].second=a; 12 xma[x]=xmi[x]; 13 yma[x]=ymi[x]; 14 return; 15 } 16 int mid=(a+b)>>1; 17 build(x<<1,a,mid),build(x<<1|1,mid+1,b); 18 xmi[x]=min(xmi[x<<1],xmi[x<<1|1]); 19 xma[x]=max(xma[x<<1],xma[x<<1|1]); 20 ymi[x]=min(ymi[x<<1],ymi[x<<1|1]); 21 yma[x]=max(yma[x<<1],yma[x<<1|1]); 22 } 23 24 P askxmi(int x,int a,int b,int c,int d){ 25 if(c>d)return P(inf,0); 26 if(c<=a&&b<=d)return xmi[x]; 27 int mid=(a+b)>>1; 28 P t(inf,0); 29 if(c<=mid)t=askxmi(x<<1,a,mid,c,d); 30 if(d>mid)t=min(t,askxmi(x<<1|1,mid+1,b,c,d)); 31 return t; 32 } 33 P askymi(int x,int a,int b,int c,int d){ 34 if(c>d)return P(inf,0); 35 if(c<=a&&b<=d)return ymi[x]; 36 int mid=(a+b)>>1; 37 P t(inf,0); 38 if(c<=mid)t=askymi(x<<1,a,mid,c,d); 39 if(d>mid)t=min(t,askymi(x<<1|1,mid+1,b,c,d)); 40 return t; 41 } 42 P askxma(int x,int a,int b,int c,int d){ 43 if(c>d)return P(-inf,0); 44 if(c<=a&&b<=d)return xma[x]; 45 int mid=(a+b)>>1; 46 P t(-inf,0); 47 if(c<=mid)t=askxma(x<<1,a,mid,c,d); 48 if(d>mid)t=max(t,askxma(x<<1|1,mid+1,b,c,d)); 49 return t; 50 } 51 P askyma(int x,int a,int b,int c,int d){ 52 if(c>d)return P(-inf,0); 53 if(c<=a&&b<=d)return yma[x]; 54 int mid=(a+b)>>1; 55 P t(-inf,0); 56 if(c<=mid)t=askyma(x<<1,a,mid,c,d); 57 if(d>mid)t=max(t,askyma(x<<1|1,mid+1,b,c,d)); 58 return t; 59 } 60 inline int cal(int x,int y,int z){ 61 return max( 62 max(askxma(1,1,n,x,z-1).first,askxma(1,1,n,z+1,y).first)-min(askxmi(1,1,n,x,z-1).first,askxmi(1,1,n,z+1,y).first) 63 , 64 max(askyma(1,1,n,x,z-1).first,askyma(1,1,n,z+1,y).first)-min(askymi(1,1,n,x,z-1).first,askymi(1,1,n,z+1,y).first) 65 ); 66 } 67 int main(){ 68 scanf("%d%d",&n,&m); 69 build(1,1,n); 70 while(m--){ 71 scanf("%d%d",&x,&y); 72 ans=cal(x,y,askxmi(1,1,n,x,y).second); 73 ans=min(ans,cal(x,y,askxma(1,1,n,x,y).second)); 74 ans=min(ans,cal(x,y,askymi(1,1,n,x,y).second)); 75 ans=min(ans,cal(x,y,askyma(1,1,n,x,y).second)); 76 printf("%d\n",ans); 77 } 78 }