3.17-3.18比賽記錄

        假期沒有出去……剛好17號晚上有一場8:00-9:40的ARC092很是適合衡中做息時間,因而就去打了,雖然是英文題面可是題意仍是十分好懂,大概是我很喜歡的類型。18號晚上的UR17是你們集體參賽,第一次打UOJ的比賽果真很是interesting。考慮到罰時問題以及感受本身再怎麼樣也寫不了不少分,最開始就把B題和C題的騙分程序交上去了,A題提交大概也在九點以前。由於是集體比賽就比較正規,19號大半天都用來改題了。難度上固然是UR大一些,不過兩場比賽各有特色,選手體驗都不錯?都是第一次參賽因此都沒有掉Rating,ARC只A了C題由於罰時少拿了553Rating,UR儘管只有10+5+10仍是+68。ios

 

ARC092

比賽連接官方題解算法

C

題意:是給出紅點藍點各$n$個,若是一個紅點橫縱座標都小於一個藍點則它們能夠配對,每一個點只能用一次,問最多有多少對。$n<=100$網絡

題解:這題難道不是在開玩笑嗎……二分圖網絡流隨便跑啊……官方題解給出的是貪心,按橫座標遞增處理藍點,對於每個藍點選可行紅點中縱座標最大的。ide

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 const int sj=110;
 7 const int inf=0x7fffffff;
 8 int n,h[sj<<1],s,t,dep[sj<<1],ans,e;
 9 queue<int> q;
10 struct point 
11 {
12     int xi,yi;
13     void in()
14     {  scanf("%d%d",&xi,&yi);  }
15 }p1[sj],p2[sj];
16 struct B
17 {
18     int ne,v,w;
19 }b[30000];
20 void add(int x,int y,int z)
21 {
22     b[e].v=y,b[e].ne=h[x],b[e].w=1,h[x]=e++;
23     b[e].v=x,b[e].ne=h[y],b[e].w=0,h[y]=e++;
24 }
25 bool bfs(int x)
26 {
27     while(!q.empty())  q.pop();
28     memset(dep,0,sizeof(dep));
29     q.push(x),dep[x]=1;
30     while(!q.empty())
31     {
32         x=q.front(),q.pop();
33         for(int i=h[x];i!=-1;i=b[i].ne)
34             if(b[i].w&&!dep[b[i].v])
35             {
36                 dep[b[i].v]=dep[x]+1;
37                 if(b[i].v==t)  return 1;
38                 q.push(b[i].v);
39             }
40     }
41     return 0;
42 }
43 inline int minn(int x,int y)
44 {
45     return x<y?x:y;
46 }
47 int dfs(int x,int f)
48 {
49     if(x==t)  return f;
50     int ret=0,d;
51     for(int i=h[x];i!=-1;i=b[i].ne)
52         if(b[i].w&&dep[b[i].v]==dep[x]+1)
53         {
54             d=dfs(b[i].v,minn(f,b[i].w));
55             ret+=d,f-=d;
56             b[i].w-=d,b[i^1].w+=d;
57             if(!f)  break;
58         }
59     if(!ret)  dep[x]=-1;
60     return ret;
61 }
62 int main()
63 {
64     scanf("%d",&n);
65     t=2*n+1;
66     memset(h,-1,sizeof(h));
67     for(int i=1;i<=n;i++)  p1[i].in(),add(s,i,1);
68     for(int i=1;i<=n;i++)  p2[i].in(),add(i+n,t,1);
69     for(int i=1;i<=n;i++)
70         for(int j=1;j<=n;j++)
71             if(p1[i].xi<p2[j].xi&&p1[i].yi<p2[j].yi)
72                 add(i,j+n,1);
73     while(bfs(s))  ans+=dfs(s,inf);
74     printf("%d",ans);
75     return 0;
76 }
C

 

D

題意:給出兩個長度爲$n$的序列$a$、$b$,把它們每一位兩兩加和($a_1+b_1$、$a_1+b_2$、$a_1+b_3$……$a_1+b_n$、$a_2+b_1$……$a_n+b_n$)獲得$n^2$個和,問這$n^2$個和的異或。$n<=2e5$,序列中數$<=2^{28}$優化

題解:按位考慮,把兩個序列都變成模$2^k$意義下,把$a$排序去掃$b$。對於$b$的每一位有影響的$a$必定是2個連續區間(注意加法有進位,因此有$2$個),二分找到這些區間就能夠了。spa

 

E

題意:給出長度爲$n$的一個序列,從中選擇元素。若是選的是兩端直接刪去,不然把這個數兩邊的元素加和取代它,再把兩邊刪去($a$、$b$、$c$、$d$、$e$選擇$c$以後變成$a$、$b+d$、$e$)。最後只剩一個元素,要使這個元素最大,輸出最優解並給出方案。給出方案即輸出要選擇多少次而且給出每次選擇的下標,注意若是前面的一個元素被刪去後面的下標要前移。$n<=1000$3d

題解:剛開始以爲這題和《寄語》一個套路,後來注意到只有下標奇偶相同的才能同時留下。輸出方案的細節挺多……要注意不能一個元素都不留。先把兩邊想刪的刪完,再把中間兩邊都不留或兩邊都留的元素依次刪去。比賽的時候一直在用隊列和棧來實現這個過程,如今看來或許仍是鏈表更合適些,畢竟數據範圍是很小的能夠掃屢次。rest

  

F

題意:給出$n$個點$m$條邊的一個有向圖,對於每一條邊詢問它反向後強連通份量數會不會變。$n<=1000,m<=2e5$code

題解:感受這好像是個很普通的問題,可是原來一直沒有想過?果真我熟悉的算法也是有不少沒有被發現的奇妙之處的。對於每條邊$u->v$求解兩個問題:$v$能不能到$u$?$u$不通過這條邊能不能到$v$?若是這兩個問題答案相同則強連通份量數不會變,不然會變。第一個問題只要$n*m$搜一下就能夠了。第二個問題,先把$u$標記,再依次搜它的每一條出邊,對於全圖中的全部點記錄它第一次被訪問是在搜哪一條出邊時;按和第一次相反的順序再搜全部出邊,給圖中的點打上第二個標記。若是$u$的出邊中有哪條邊另外一個端點有不是它本身的標記,這條邊的第二個問題答案爲$true$,複雜度仍爲$n*m$。blog

 

 

UR17

比賽連接官方題解。UOJ上的題不想敘述題意了……這個題目風格和AtCoder大概正好相反。

A

       百年不遇的FST好題,我在內的一大波HZOI羣衆都覺得部分分是假的性質是真的,今天早起咱們發現咱們是假的。因爲對這個性質的不肯定我壓根就沒有去寫$n^2$如下的算法,因此在你們都認爲本身能A的時候我十分傷心……傷心了整整一夜呢。不過即便這$25$分也沒有拿全,畢竟我沒有寫$nlog$作法啊。對於這個貪心的反例構造大概是人類智慧所不能及的,手玩失敗以後試圖拍了半天也沒拍出來,UOJ上大概幾萬的數據也不過差了$1$或$2$。實在是妙啊。

       題解上說$n^n$枚舉能過Subtask1,可是我不剪枝並不能過。鏈上的分$n!$聽說能過Subtask2,可是我寫了以後只過了Subtask1……大概暴力也是須要優化的,隨便地寫暴力得分也很隨便啊。稍做分析就能發現答案必定是鏈,反正多與一些總沒壞處。可是後面那個字典序最小就不必定了,感受出題人這個先下誘餌再挖陷阱玩得真是好啊……不過部分分仍是要寫的,Subtask3能夠$n^2$枚舉,Subtask4能夠先處理全部數共有的位,把這些位提早累計到答案裏以後最多$log$次與值就會變爲$0$了,因此複雜度爲$nlogn$。

       脫離那些亂搞去研究真正的正解,依然把共有的位提出來,$f[i]$表示把$i$消到$0$的最小代價,初始化$f[0]=0$,$f[a[i]]=a[i]]$,那麼$f[i]=min(f[i],f[i$&$a[j]]+(i$&$a[j]))$。設$m$爲序列中元素的最大值,這個算法複雜度爲$n*m$,能夠經過Subtask5。如今咱們採用更快速的消去方式,每次消一個集合,預處理$vi[i]$表示是否存在$j$使得$i$&$a[j]$爲$0$,轉移$f$時枚舉$i$的子集$j$,若是$vi[j]==1$則$f[i]=min(f[i],f[i$^$j]+(i$^$j))$,一個比較顯然的剪枝是當$(i$^$j)>=f[i]$時break。我不是很會分析最後這個解法的複雜度……反正能過還跑得挺快?這漫長的一道題終於結束了。

UPD:我終於從wq那裏看到了一個貪心的反例:

3
11 12  19

(1011,、1100、10011)

若是貪心會先選11,而後19,而後12,獲得的答案是11+3+0=14;但若是先選12再選19,答案就只有12了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define ll long long
 6 using namespace std;
 7 const int sj=100010;
 8 const ll inf=1ll<<50;
 9 int n,a[sj],fa[sj],tp,xr,bin[20];
10 bool vi[sj];
11 ll ans,nt,mi;
12 void check()
13 {
14     nt=xr=a[fa[1]];
15     for(int i=2;i<=n;++i)
16     {
17         xr&=a[fa[i]];
18         nt+=xr;
19         if(nt>=ans)  return;
20     }
21     ans=nt;
22 }
23 void dfs(int x)
24 {
25     if(x==n+1)
26     {
27         check();
28         return;
29     }
30     for(int i=1;i<=n;i++)
31         if(!vi[i])
32         {
33             fa[x]=i,vi[i]=1;
34             dfs(x+1);
35             vi[i]=0;
36         }
37 }
38 void work1()
39 {
40     bin[0]=1;
41     for(int i=1;i<=19;i++)  bin[i]=bin[i-1]<<1;
42     sort(a+1,a+n+1);
43     for(int i=0;i<=18;i++)
44     {
45         tp=1;
46         for(int j=1;j<=n;j++)
47             if(!(bin[i]&a[j]))
48             {  tp=0;break;  }
49         if(tp)  
50         {    
51             ans+=1ll*n*bin[i];
52             tp=bin[i]^(bin[19]-1);
53             for(int j=1;j<=n;j++)
54                 a[j]&=tp;
55         }
56     }
57     xr=a[1],ans+=a[1];
58     for(int j=2;j<=n;j++)
59     {
60         mi=inf;
61         for(int k=1;k<=n;k++)
62             if((xr&a[k])<mi)
63                 mi=a[k]&xr;
64         xr=mi,ans+=xr;
65         if(!xr)  break;
66     }
67     printf("%lld",ans);
68 }
69 void work2()
70 {
71     ans=inf;
72     dfs(1);
73     printf("%lld",ans);
74 }
75 int main() 
76 {
77     scanf("%d",&n);
78     for(int i=1;i<=n;i++)  scanf("%d",&a[i]);
79     if(n>12)   work1();
80     else       work2();
81     return 0;
82 }
subtask一、三、4
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define ll long long
 6 const int sj=100010;
 7 int n,a[sj],bin[20],op,tp,op2;
 8 ll f[sj<<1],ans;
 9 ll minn(ll x,ll y)
10 {
11     if(y<0)  return x;
12     return x<y?x:y;
13 }
14 int main()
15 {
16     scanf("%d",&n);
17     for(int i=1;i<=n;i++)   scanf("%d",&a[i]);
18     bin[0]=1;
19     for(int j=1;j<=19;j++)  bin[j]=bin[j-1]<<1;
20     for(int j=0;j<=18;j++)
21     {
22         op=op2=1;
23         for(int i=1;i<=n;i++)
24         {
25             if(!(bin[j]&a[i]))  op=0;
26             else                op2=0;
27         }
28         if(op)
29         {
30             ans+=1ll*n*bin[j];
31             for(int i=1;i<=n;i++)
32                 a[i]&=bin[j]^(bin[19]-1);
33         }
34         else if(!op2)  tp|=bin[j];
35     }
36     memset(f,0x7f,sizeof(f));
37     for(int i=1;i<=n;i++)  f[a[i]]=a[i];
38     f[0]=0;
39     for(int j=1;j<=tp;j++)
40         for(int i=1;i<=n;i++)
41             f[j]=minn(f[j],f[j&a[i]]+(j&a[i]));
42     ans+=f[tp];
43     printf("%lld",ans);
44     return 0;
45 }
subtask5
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define ll long long
 6 using namespace std;
 7 const int sj=200010;
 8 int n,a[sj],bin[20],op,tp,op2;
 9 bool vi[sj<<1];
10 ll f[sj<<1],ans;
11 ll minn(ll x,ll y)
12 {
13     if(y<0)  return x;
14     return x<y?x:y;
15 }
16 int main()
17 {
18     bin[0]=1;
19     for(int j=1;j<=19;j++)  bin[j]=bin[j-1]<<1;
20     op=bin[19]-1;
21     scanf("%d",&n);
22     for(int i=1;i<=n;i++)   
23     {    
24         scanf("%d",&a[i]);
25         op&=a[i],tp|=a[i];
26     }
27     ans=1ll*n*op,tp^=op;
28     memset(f,0x7f,sizeof(f));
29     for(int i=1;i<=n;i++)  
30     {
31         a[i]^=op,f[a[i]]=a[i];
32         vi[tp^a[i]]=1;
33     }
34     vi[0]=1;
35     for(int j=tp;j>=1;j--)
36         for(int i=0;i<=18;i++) 
37             if((j|bin[i])<=tp)
38                 vi[j]|=vi[j|bin[i]];
39     f[0]=0;
40     for(int j=1;j<=tp;j++)
41         for(int i=j;i;(--i)&=j)
42         {
43             if(f[j]<=(j^i))  break;
44             if(vi[i])  f[j]=minn(f[j],f[j^i]+(j^i));
45         }
46     ans+=f[tp];
47     printf("%lld",ans);
48     return 0;
49 }
A

 

B

       比賽的時候看完題就想說計算幾何我拒絕……不過改題的過程當中發現它並非普通的計算幾何,只是用到了一些幾何知識;彷佛暑假裏講計算幾何的時候學長說這個知識點很難和其餘板塊聯繫起來,如今看來這種狀況是在漸漸被改變吧。

       Subtask1是開玩笑的輸出兩點之間距離……更搞笑的是樣例二的答案也是兩點之間距離……惟一有意義的部分分是Subtask4,保證一直有一點在端點,考試的時候理解的彷彿是一個點一直在葉子這也是十分有毒……二分答案,用$f[i][j]$表示一點在$i$另外一點在$j$是否可行,初始化$f[stx][sty]=1$。只要一點在端點另外一點移動時最大距離必定是兩點距離,從初始狀態向外選擇知足限制的狀態DFS便可,複雜度$n^2log$。

       從剛纔那個算法拓展出正解,如今兩點能夠同時在邊上移動,咱們要加入邊的狀態了。用$f[i][j]$表示一點在$i$另外一點在邊$j$上離$i$最近的點是否可行,初始化就是點$stx$和與$sty$相連的全部邊均可行,反向同理。轉移仍是引用吧……感受不能比原題解說得更清楚了

f[i][j]爲1當且僅當點i到邊j的最短距離≤mid且存在某個f[k][x]爲1,其中k是j的某個端點,x是某個以i爲端點的邊;或存在某個f[t][j]爲1,其中t與i有邊相連。直接BFS轉移就能夠了。

因此所謂的幾何知識就是求點到線段距離啦……我分類討論得很麻煩,並且是用DFS實現的。複雜度仍是$n^2log$的。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #define eps 1e-8
 6 using namespace std;
 7 const int sj=1010;
 8 int n,stx,sty,h[sj],e,a1,a2,d[sj];
 9 double le,ri,mid;
10 bool vi[sj][sj];
11 struct point
12 {
13     double xa,ya;
14 }p[sj];
15 struct B
16 {
17     int ne,v;
18 }b[sj<<1];
19 void add(int x,int y)
20 {
21     b[e].v=y,b[e].ne=h[x],h[x]=e++;
22     b[e].v=x,b[e].ne=h[y],h[y]=e++;
23 }
24 double dis(point x,point y)
25 {
26     double ret=(x.xa-y.xa)*(x.xa-y.xa)+(x.ya-y.ya)*(x.ya-y.ya);
27     return sqrt(ret);
28 }
29 bool dfs(int x,int y)
30 {
31     if(d[x]==1&&d[y]==1)  return 1;
32     if(vi[x][y])          return 0;
33     vi[x][y]=vi[y][x]=1;
34     for(int i=h[x];i!=-1;i=b[i].ne)
35         if(dis(p[b[i].v],p[y])<=mid)
36             if(dfs(b[i].v,y))
37                 return 1;
38     for(int i=h[y];i!=-1;i=b[i].ne)
39         if(dis(p[b[i].v],p[x])<=mid)
40             if(dfs(b[i].v,x))
41                 return 1;
42     return 0;
43 }
44 int main()
45 {
46     memset(h,-1,sizeof(h));
47     scanf("%d%d%d",&n,&stx,&sty);
48     for(int i=1;i<=n;i++)  scanf("%lf%lf",&p[i].xa,&p[i].ya);
49     for(int i=1;i<n;i++)
50     {
51         scanf("%d%d",&a1,&a2);
52         add(a1,a2),d[a1]++,d[a2]++;
53     }
54     if(d[stx]==1&&d[sty]==1)
55     {
56         printf("%.10lf",dis(p[stx],p[sty]));
57         return 0;
58     }
59     le=dis(p[stx],p[sty]),ri=2e9;
60     while(le<ri-eps)
61     {
62         mid=(le+ri)/2.0;
63         memset(vi,0,sizeof(vi));
64         if(dfs(stx,sty))  ri=mid;
65         else              le=mid;
66     }
67     printf("%.10lf",le);
68     return 0;
69 }
subtask一、4
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #define eps 1e-8
  6 using namespace std;
  7 const int sj=1010;
  8 int n,stx,sty,h[sj],e,a1,a2,d[sj];
  9 bool vi[sj][sj];
 10 double le,ri,mid;
 11 struct point
 12 {
 13     double xa,ya;
 14     friend point operator - (point x,point y)
 15     {  return (point){x.xa-y.xa,x.ya-y.ya};  }
 16     friend double operator * (point x,point y)
 17     {  return x.xa*y.ya-x.ya*y.xa;  } 
 18 }p[sj];
 19 struct B
 20 {
 21     int ne,v,u;
 22 }b[sj<<1];
 23 void add(int x,int y)
 24 {
 25     b[e].u=x,b[e].v=y,b[e].ne=h[x],h[x]=e++;
 26     b[e].u=y,b[e].v=x,b[e].ne=h[y],h[y]=e++;
 27 }
 28 double dis(point x,point y)
 29 {
 30     double ret=(x.xa-y.xa)*(x.xa-y.xa)+(x.ya-y.ya)*(x.ya-y.ya);
 31     return sqrt(ret);
 32 }
 33 double minn(double x,double y)
 34 {
 35     return x<y?x:y;
 36 }
 37 double abss(double x)
 38 {
 39     return x>0?x:-x;
 40 }
 41 double di(point x,point y,point z)
 42 {
 43     double d1=z.xa-y.xa,d2=z.ya-y.ya;
 44     if(d1==0)
 45     {
 46         if((x.ya-y.ya)*(x.ya-z.ya)<=0)
 47             return abss(y.xa-x.xa);
 48         return minn(dis(x,z),dis(x,y));
 49     }
 50     if(d2==0)
 51     {
 52         if((x.xa-y.xa)*(x.xa-z.xa)<=0)
 53             return abss(y.ya-x.ya);
 54         return minn(dis(x,z),dis(x,y));
 55     }
 56     double xi=(y.ya-d2/d1*y.xa-x.ya+d1/(-d2)*x.xa)/(d1/(-d2)-d2/d1);
 57     if((y.xa-xi)*(z.xa-xi)>0)  return minn(dis(x,z),dis(x,y));
 58     double dot=(y-x)*(z-x);
 59     if(dot<0)  dot=-dot;
 60     return dot/dis(y,z);
 61 }
 62 bool dfs(int x,int y)
 63 {
 64     if(vi[x][y])    return 0;
 65     vi[x][y]=1;
 66     int u1=b[x<<1].u,v1=b[x<<1].v;
 67     if(d[u1]==1&&d[y]==1)
 68         if(dis(p[u1],p[y])<=mid)
 69             return 1;
 70     if(d[v1]==1&&d[y]==1)
 71         if(dis(p[v1],p[y])<=mid)
 72             return 1;
 73     for(int i=h[y];i!=-1;i=b[i].ne)
 74     {
 75         if(di(p[u1],p[b[i].u],p[b[i].v])<=mid&&dfs(i/2,u1))  return 1;
 76         if(di(p[v1],p[b[i].u],p[b[i].v])<=mid&&dfs(i/2,v1))  return 1;
 77         if(di(p[b[i].v],p[u1],p[v1])<=mid&&dfs(x,b[i].v))    return 1;
 78     }
 79     return 0;
 80 }
 81 bool check(double mid)
 82 {
 83     memset(vi,0,sizeof(vi));
 84     for(int i=h[stx];i!=-1;i=b[i].ne)
 85         if(dfs(i/2,sty))
 86             return 1;
 87     for(int i=h[sty];i!=-1;i=b[i].ne) 
 88         if(dfs(i/2,stx))
 89             return 1;
 90     return 0;
 91 }
 92 int main()
 93 {
 94     memset(h,-1,sizeof(h)),e=2;
 95     scanf("%d%d%d",&n,&stx,&sty);
 96     for(int i=1;i<=n;i++)  scanf("%lf%lf",&p[i].xa,&p[i].ya);
 97     for(int i=1;i<n;i++)
 98     {
 99         scanf("%d%d",&a1,&a2);
100         add(a1,a2),d[a1]++,d[a2]++;
101     }
102     if(d[stx]==1&&d[sty]==1)
103     {
104         printf("%.10lf",dis(p[stx],p[sty]));
105         return 0;
106     }
107     le=dis(p[stx],p[sty]),ri=2e9;
108     while(le<ri-eps)
109     {
110         mid=(le+ri)/2.0;
111         if(check(mid))  ri=mid;
112         else            le=mid;
113     }
114     printf("%.10lf",le);
115     return 0;
116 }
B

 

C

      看到那個均勻分佈就感受這題不可作,並且至今我也不會改……考場上的騙分部分只有Subtask1,每條邊都有一端爲$1$則答案$=1$的指望$+max(2,3,4,5…n)$的指望$=\frac{1}{2}+\frac{n-1}{n}$。積分這個東西確實不大會啊。

相關文章
相關標籤/搜索