【Codeforces #168 Div1 & Div2】Solutions

  转载请注明出处:http://www.cnblogs.com/Delostik/archive/2013/02/21/2920087.html

  上一次失误掉进Div2,这次也很可惜,B题读错了题又差一点。。。

【A.Lights Out】

  http://www.codeforces.com/contest/275/problem/A

  题目大意:按开关会导致周围相邻开关状态一起变化,给出操作问结果。

  简单模拟

View Code
 1 #include <iostream>
 2 using namespace std;
 3 
 4 const int dx[5]={0,1,0,-1,0};
 5 const int dy[5]={1,0,-1,0,0};
 6 int res[5][5],x;
 7 
 8 int main(){
 9     for(int i=1;i<=3;i++)
10         for(int j=1;j<=3;j++){
11             cin>>x;
12             for(int k=0;k<5;k++)
13                 res[i+dx[k]][j+dy[k]]+=x;
14         }
15     for(int i=1;i<=3;i++){
16         for(int j=1;j<=3;j++)
17             cout<<(res[i][j]+1)%2;
18         cout<<endl;
19     }
20     return 0;
21 }

【B.Convex Shape】

  http://www.codeforces.com/contest/275/problem/B

  题目大意:判断所给图形是不是所谓"Convex Shape"(即任意两个格子互达且只需转向一次)

  枚举两个格子,沿着路径走就可以了= =竟然读错题了啊囧

View Code
 1 #include <iostream>
 2 using namespace std;
 3 
 4 int n,m,x[3000],y[3000],cnt;
 5 char s[50][50];
 6 
 7 bool check(int x,int a1,int b1,int y,int a2,int b2){
 8     for(int i=min(a1,b1);i<=max(a1,b1);i++)
 9         if(s[x][i]!='B') return 0;
10     for(int i=min(a2,b2);i<=max(a2,b2);i++)
11         if(s[i][y]!='B') return 0;
12     return 1;
13 }
14 
15 int main(){
16     cin>>n>>m;
17     for(int i=0;i<n;i++){
18         cin>>s[i];
19         for(int j=0;j<m;j++)
20             if(s[i][j]=='B') x[++cnt]=i,y[cnt]=j;
21     }
22     for(int i=1;i<cnt;i++)
23         for(int j=i+1;j<=cnt;j++){
24             if(check(x[i],y[i],y[j],y[j],x[i],x[j])) continue;
25             if(check(x[j],y[i],y[j],y[i],x[i],x[j])) continue;
26             cout<<"NO";
27             return 0;
28         }
29     cout<<"YES";
30     return 0;
31 }

【C.k-Multiple Free Set】(A in Div1)

  http://www.codeforces.com/contest/275/problem/C

  http://www.codeforces.com/contest/274/problem/A

  题目大意:从所给数集中找出最大子集,使得子集中不存在x,y,使得y=x*k。求最大子集的大小。

  我思路大体是这样的。。。

  首先我把题目抽象成一个图的问题,对于集合中x,x*k,将他们连边,最后图中会形成若干条链,不同的链一定互不干扰,问题转换成在每条链中选取尽量多的点。

  到这里就可以做了(博主就是这样做的结果时间内存都巨大),但是可以继续想

  选取尽量多的点的一种方法就是:链头上的点先选,然后隔一个点选一个。这样就有了一种贪心的方法,先将集合中数字排序,然后如果该数可选,就选,不可选就不选。

  代码1:贪心方法

View Code
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <map>
 4 using namespace std;
 5 
 6 map<long long,bool> m;
 7 int n,ans;
 8 long long k,a[100010];
 9 
10 int main(){
11     cin>>n>>k;
12     for(int i=0;i<n;i++)
13         cin>>a[i];
14     sort(a,a+n);
15     for(int i=0;i<n;i++)
16         if(!m[a[i]]) m[a[i]]=m[a[i]*k]=true;
17     ans=(k==1)?m.size():m.size()/2;
18     cout<<ans;
19     return 0;
20 }

  代码2:图方法

View Code
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <map>
 5 #define mn 100010
 6 #define mm 200020
 7 using namespace std;
 8 typedef long long ll;
 9 
10 map<long long,int> m;
11 int cnt[100010],n,ans,tot;
12 long long k,a[100010];
13 bool vis[100010];
14 
15 struct EDGE{
16     int pnt;
17     EDGE *pre;
18     EDGE(){}
19     EDGE(int _pnt,EDGE *_pre):pnt(_pnt),pre(_pre){}
20 }Edge[mm],*SP=Edge,*edge[mn];
21 
22 inline void addedge(int a,int b){
23     edge[a]=new(++SP)EDGE(b,edge[a]);
24     edge[b]=new(++SP)EDGE(a,edge[b]);
25 }
26 
27 int dfs(int x){
28     vis[x]=true;
29     for(EDGE *j=edge[x];j;j=j->pre)
30         if(!vis[j->pnt]){
31             cnt[x]+=dfs(j->pnt)    ;
32         }
33     return cnt[x];
34 }    
35 
36 int main(){
37     cin>>n>>k;
38     for(int i=1;i<=n;i++){
39         scanf("%d",&a[i]);
40         cnt[i]=1;
41     }
42     sort(a+1,a+1+n);
43     for(int i=1;i<=n;i++)
44         if(!m[a[i]]) m[a[i]]=++tot;
45     n=tot;
46     for(int i=1;i<=n;i++){
47         int tmp=m[a[i]*k];
48         if(tmp>0) addedge(m[a[i]],tmp);
49     }
50     for(int i=1;i<=n;i++)
51         if(!vis[i]){
52             dfs(i);
53             ans+=(cnt[i]+1)/2;
54         }
55     cout<<ans<<endl;
56     return 0;
57 }

【D.Zero Tree】(B in Div1)

  http://www.codeforces.com/contest/275/problem/D

  http://www.codeforces.com/contest/274/problem/B

  题目大意,给定一棵带权树,每次可以选取包括节点1的一棵子树,使其所有点权±1,问最少操作次数使得所有节点点权为0.

  显然应该先把叶子节点变成0,然后依次向上做。dfs即可

View Code
 1 #include <iostream>
 2 #define mn 100010
 3 #define mm 200010
 4 using namespace std;
 5 typedef long long ll;
 6 template<class T>inline void gmax(T &a,T b){if(a<b)a=b;}
 7 
 8 struct EDGE{
 9     int pnt;
10     EDGE *pre;
11     EDGE(){}
12     EDGE(int _pnt,EDGE *_pre):pnt(_pnt),pre(_pre){}
13 }Edge[mm],*SP=Edge,*edge[mn];
14 
15 ll add[mn],sub[mn],w[mn];
16 int n,a,b;
17 
18 inline void addedge(int a,int b){
19     edge[a]=new(++SP)EDGE(b,edge[a]);
20     edge[b]=new(++SP)EDGE(a,edge[b]);
21 }
22 
23 void dfs(int x,int fa){
24     ll t_add=0,t_sub=0;
25     for(EDGE *j=edge[x];j;j=j->pre)
26         if(j->pnt!=fa){
27             dfs(j->pnt,x);
28             gmax(t_add,add[j->pnt]);
29             gmax(t_sub,sub[j->pnt]);
30         }
31     w[x]+=t_add-t_sub;
32     if(w[x]<0) t_add-=w[x];
33     else t_sub+=w[x];
34     add[x]=t_add,sub[x]=t_sub;
35 }
36 
37 int main(){
38     cin>>n;
39     for(int i=1;i<n;i++){
40         cin>>a>>b;
41         addedge(a,b);
42     }
43     for(int i=1;i<=n;i++)
44         cin>>w[i];
45     dfs(1,0);
46     cout<<sub[1]+add[1]<<endl;
47     return 0;
48 }

【E.】The Last Hole!(C in Div 1)

  http://www.codeforces.com/contest/275/problem/E

  http://www.codeforces.com/contest/274/problem/C

  题如其名,最后一题是个坑。。。等会写

【D.Lovely Matrix】(Div 1)

  http://www.codeforces.com/contest/274/problem/D

  题目大意:n×m格子,格子有权值,有空格,问是否可以交换某些列的次序,使得每一行上的数字不减。

  显然是拓扑排序。问题在于数据范围有点大,如果出现权值相同的格子,建图时逐个连边复杂度非常高。我们可以考虑附加点P[x],使权值为x的点都连向P[x],然后由P[x]连向比x大的点。

View Code
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <queue>
 4 #define mm 200010
 5 #define mn 400010
 6 using namespace std;
 7 
 8 queue<int> q;
 9 int n,m,ans[mn],deg[mn],cnt,idx;
10 struct REC{int v,p;}a[mn];
11 
12 struct EDGE{
13     int pnt;
14     EDGE *pre;
15     EDGE(){}
16     EDGE(int _pnt,EDGE *_pre):pnt(_pnt),pre(_pre){}
17 }Edge[mm],*SP=Edge,*edge[mn];
18 
19 inline void addedge(int a,int b){
20     edge[a]=new(++SP)EDGE(b,edge[a]);
21     deg[b]++;
22 }
23 
24 inline bool cmp(REC a,REC b){return a.v<b.v;}
25 
26 int main(){
27     cin>>n>>m;
28     for(int i=0;i<n;i++){
29         for(int j=0;j<m;j++){
30             cin>>a[j].v;
31             a[j].p=j;
32         }
33         sort(a,a+m,cmp);
34         for(int j=0;j<m;j++)
35             if(a[j].v!=-1){
36                 if(!j || a[j].v!=a[j-1].v) cnt++;
37                 addedge(a[j].p,m+cnt+1);
38                 addedge(m+cnt,a[j].p);
39             }
40         cnt++;
41     }
42     for(int i=0;i<m+cnt;i++)
43         if(!deg[i]) q.push(i);
44     while(!q.empty()){
45         int i=q.front();q.pop();
46         if(i<m) ans[++idx]=i;
47         for(EDGE *j=edge[i];j;j=j->pre)
48             if(!--deg[j->pnt]) q.push(j->pnt);
49     }
50     if(idx<m) cout<<-1;
51     else for(int i=1;i<=m;i++) 
52         cout<<ans[i]+1<<" ";
53     return 0;
54 }


【E. Mirror Room】(Div 1)

  http://www.codeforces.com/contest/274/problem/E

  题目大意:光线在镜子格里反射,问能经过多少格子。

  看了liouzhou101大神的代码明白了,其实就是用了一堆map和set然后模拟。。。。

  好不容易仿照着写出来了,但是今天CF评测机抽风,在第24个点tle,连liouzhou101的代码现在都过不了。。。

View Code
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <map>
 5 #include <set>
 6 #define x first
 7 #define y second
 8 using namespace std;
 9 typedef pair<int,int> PII;
10 
11 int n,m,k,ans,x,y;
12 set<PII> block,empty_cell;
13 map<PII,set<PII> > vis;
14 PII s,t,dir;
15 char sym[5];
16 
17 int main(){
18     cin>>n>>m>>k;
19     for(int i=0;i<=n+1;i++)
20         block.insert(PII(i,0)),
21         block.insert(PII(i,m+1));
22     for(int i=0;i<=m+1;i++)
23         block.insert(PII(0,i)),
24         block.insert(PII(n+1,i));
25     while(k--){
26         scanf("%d%d",&x,&y);
27         block.insert(PII(x,y));
28     }
29     scanf("%d%d",&s.x,&s.y);
30     scanf("%s",sym);
31     dir=PII(sym[0]=='N'?-1:1,sym[1]=='W'?-1:1);
32     while(true){
33         if(empty_cell.insert(s).y) ans++;
34         t=PII(s.x+dir.x,s.y+dir.y);
35         if(!block.count(t)){
36             s=t;
37             continue;
38         }
39         if(!vis[t].insert(dir).y) break;
40         x=block.count(PII(t.x-dir.x,t.y)),y=block.count(PII(t.x,t.y-dir.y));
41         if (x==y) dir.x=-dir.x,dir.y=-dir.y;
42         else if(x) s.x+=dir.x,dir.y=-dir.y;
43         else s.y+=dir.y,dir.x=-dir.x;
44     }
45     cout<<ans<<endl;
46     return 0;
47 }

  

原文地址:https://www.cnblogs.com/Delostik/p/2920087.html