1. 程式人生 > >North American Invitational Programming Contest 2018

North American Invitational Programming Contest 2018

cst 有根樹 替換 set spf 如果 查詢 很好 str

題目鏈接: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 }
View Code

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 }
View Code

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 }
View Code

North American Invitational Programming Contest 2018