【jsoi】第一季 [略]精简题解

UPD:好像有两道题的代码逃跑了?= =就先不找了,反正都是水题。


 

精简题解系列第四弹。(其实也不是那么精简啊= =)

[JSOI2008]最大数maxnumber

  单点修改,区间最大值查询,裸线段树

 1 /**************************************************************
 2     Problem: 1012
 3     User: wsc500
 4     Language: C++
 5     Result: Accepted
 6     Time:944 ms
 7     Memory:5368 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <cstdlib>
12 #include <iostream>
13 #include <cstring>
14 #include <algorithm>
15  
16 using namespace std;
17 #define MAXN (262144+2)
18 #define Q (262143)
19 #define bigger(a,b) ((a)>(b)?(a):(b))
20  
21 /*Gloable*/
22 int m,d;
23 int T[MAXN*4];
24 /*Function*/
25 void insert(int p,int val){
26     T[p+Q]=val;
27     int i=p+Q;
28     while (i!=0){
29         T[i/2]=bigger(T[i/2*2],T[i/2*2+1]);
30         i/=2;
31     }
32 }
33 int query(int L,int R,int a,int b,int p){
34     //printf("%d %d %d %d %d\n",L,R,a,b,p);
35     if (R>b)  R=b;
36     if (L<a)  L=a;
37     if (L==a&&R==b) return T[p];
38      
39     int mid=(a+b)/2;
40     int ans1,ans2;
41     ans1=ans2=0;
42      
43     if (L<=mid) ans1=query(L,R,a,mid,p*2);
44     if (R>mid) ans2=query(L,R,mid+1,b,p*2+1);
45      
46     return bigger(ans1,ans2);
47 }
48 int main()
49 {
50     memset(T,0,sizeof(T));
51     int x,t=0,n=1;
52     char cmd[10];
53     scanf("%d%d",&m,&d);
54     for (int i=1;i<=m;i++){
55         //printf("hehe\n");
56         scanf("%s%d",cmd,&x);
57         if (cmd[0]=='A'){
58             insert(n,(x+t)%d);
59             n++;
60         }
61         if (cmd[0]=='Q'){
62             //printf("hehe");
63             t=query(n-x,n-1,1,Q+1,1);
64             printf("%d\n",t);
65         }
66     }
67      
68     return 0;
69 }
View Code

[JSOI2008]球形空间产生器sphere

  果然前两道都是水题T_T。题中给出了公式,那么列出n+1个式子果断考虑解方程。首先开方可以忽略,对于平方的问题,相邻两个等式相减,利用平方差可以吧未知数的平方约掉。那么正好n+1个式子得出n和一次方程,高斯消元即可。很少写高斯消元啊,1A真开心= =

 1 /**************************************************************
 2     Problem: 1013
 3     User: wsc500
 4     Language: C++
 5     Result: Accepted
 6     Time:0 ms
 7     Memory:1276 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <cstdlib>
12 #include <iostream>
13 #include <cstring>
14 #include <algorithm>
15 #include <cmath>
16  
17 using namespace std;
18 #define MAXN (10+5)
19 #define bigger(a,b) ((a)>(b)?(a):(b))
20  
21 /*Gloable*/
22 double data[MAXN+1][MAXN];
23 double M[MAXN][MAXN];
24 int n;
25 /*Function*/
26 void readData(){
27     scanf("%d",&n);
28     for (int i=1;i<=n+1;i++){
29         for (int j=1;j<=n;j++){
30             scanf("%lf",&data[i][j]);
31         }
32     }
33 }
34 void setGuass(){
35     for (int i=2;i<=n+1;i++){
36         for (int j=1;j<=n;j++){
37             M[i-1][j]=2.0*(data[i][j]-data[i-1][j]);
38             M[i-1][n+1]+=(data[i][j]+data[i-1][j])*(data[i][j]-data[i-1][j]);
39         }
40     }
41 }
42 void sloveGuass(){
43     for (int i=1;i<=n;i++){
44         int r=i;
45         for (int j=i+1;j<=n;j++){
46             if (fabs(M[j][i])>fabs(M[r][i]))    r=j;
47         }
48         for (int j=1;j<=n+1;j++)  swap(M[i][j],M[r][j]);
49          
50         for (int j=i+1;j<=n;j++){
51             double f=M[j][i]/M[i][i];
52             for (int k=i;k<=n+1;k++){
53                 M[j][k]-=M[i][k]*f;
54             }
55         }
56     }
57      
58     for (int i=n;i>=1;i--){
59         for (int j=i+1;j<=n;j++){
60             M[i][n+1]-=M[i][j]*M[j][n+1];
61         }
62         M[i][n+1]/=M[i][i];
63     }
64 }
65 void printAns(){
66     for (int i=1;i<=n-1;i++){
67         printf("%.3lf ",M[i][n+1]);
68     }
69     printf("%.3lf\n",M[n][n+1]);
70 }
71 int main()
72 {
73     memset(M,0,sizeof(M));
74     readData();
75     setGuass();
76     sloveGuass();
77     printAns();
78     return 0;
79 }
View Code

[JSOI2008]星球大战starwar

  进击的水题= =离线倒过来搞就是裸的并差集了

 [JSOI2008]最小生成树计数

  终于不算水题了。。可以发现一个性质:最小生成树中某种边权的边的数量是固定的,有注意到相同边权的边不超过10条,所以先求最小生成树然后2^10枚举子集,判断即可。

 [JSOI2009]游戏Game

  在图上的博弈问题。突然就想起了NOI那道[兔兔和蛋蛋的游戏],然后就明白了。都是一个模型,本质上是二分图增广路的性质。

  简单来说,如果先手放在一个【一定在最大匹配内】的点上,那么必输。为什么呢?因为这是后手可以移动到其对应的匹配点上。如果先手继续移动到匹配点上,那么后手可以沿着最大匹配的交错轨移动,所以只要先手移动,后手一定能移动,而且最后是先手不能移动(因为如果先手又能移动了,那么就得到了一条增广路,那么互换匹配边和非匹配变后最大匹配+1,因为最大匹配已经求出来了,所以显然不存在增广路,所以最后先手不能继续移动到匹配点)。如果先手移动到了未匹配点上,那么后手可以继续移动到一个匹配点,就和刚才一样了。所以可知,先手放在一个【一定在最大匹配内】的点上,那么必输。

  同理,如果先手放在一个【不一定在最大匹配内】的点上,那么就必胜了。因为这时后手不得不移动到非匹配点,如同上面的过程,就是先手占了优势,所以先手必胜。

  那么问题就简单了。把网格黑白染色,求最大匹配。

  如何判断一个点是否一定在最大匹配上呢?对于X部,从S开始沿残量网络DFS,不能走到的点就一定在最大匹配上。同理对于Y部把原图反向,从T开始DFS。

  这又是为什么呢?(= =) 还是考虑增广路,DFS的过程实际上得到了一条交错轨,那么互换匹配边与非匹配边后匹配点一定不变,因为变了就出现了增广路。所以这些点在任意一种最大匹配中都是匹配点。

  那么就完事了。

  做这道题的时候不幸发现 我的好友【智商】已经下线,所以代码就不贴了。。。反正是写了200+行的巨挫代码。

[JSOI2009]Count

  果然我还是就会做水题T_T。每种颜色开一个BIT,单点修改区间和查询。

/**************************************************************
    Problem: 1452
    User: wsc500
    Language: C++
    Result: Accepted
    Time:4516 ms
    Memory:42940 kb
****************************************************************/
 
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
 
using namespace std;
#define MAXN (300+10)
#define MAXC (100+10)
#define lowbit(i) (i&(-i))
 
/*Gloable*/
int data[MAXN][MAXN];
int m,n;
/*Function*/
struct BIT{
    int t[MAXN][MAXN];
    void add(int x,int y,int val){
        for (int i=x;i<=n;i+=lowbit(i))
            for (int j=y;j<=m;j+=lowbit(j))
                t[i][j]+=val;
    }
    int _query(int x,int y){
        int rtn=0;
        for (int i=x;i>=1;i-=lowbit(i)){
            for (int j=y;j>=1;j-=lowbit(j)){
                rtn+=t[i][j];
            }
        }
        return rtn;
    }
    int query(int xa,int ya,int xb,int yb){
        return _query(xb,yb)-_query(xb,ya-1)-_query(xa-1,yb)+_query(xa-1,ya-1);
    }
}B[MAXC];
 
void readData(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++){
            scanf("%d",&data[i][j]);
        }
    }
}
void init(){
    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++){
            B[data[i][j]].add(i,j,1);
        }
    }
}
void work(){
    int q,cmd,a,b,c,d,e;
    scanf("%d",&q);
    for (int i=1;i<=q;i++){
        scanf("%d",&cmd);
        if (cmd==1){
            scanf("%d%d%d",&a,&b,&c);
            B[data[a][b]].add(a,b,-1);
            B[c].add(a,b,1);
            data[a][b]=c;
        }
        if (cmd==2){
            scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
            printf("%d\n",B[e].query(a,c,b,d));
        }
    }
}
int main()
{
    readData();
    init();
    work();
    return 0;
}
View Code

[JSOI2009]去括号

  这是以前做的那道。好像直接乱搞就可以了?忘了。代码就不贴了

[JSOI2010]Group 部落划分 Group

  可以二分答案,然后统计块数。也可以直接给所有点对按距离从小到大排序,逐个合并直到块数<k。

 1 /**************************************************************
 2     Problem: 1821
 3     User: wsc500
 4     Language: C++
 5     Result: Accepted
 6     Time:488 ms
 7     Memory:17236 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <cstdlib>
12 #include <iostream>
13 #include <cstring>
14 #include <algorithm>
15 #include <queue>
16 #include <stack>
17 #include <vector>
18 #include <cmath>
19  
20 using namespace std;
21 #define MAXN (1000+10)
22 #define minner(a,b) ((a)<(b)?(a):(b))
23  
24 /*Gloable*/
25 struct pos{
26     double x,y;
27     pos(double x=0.0,double y=0.0):x(x),y(y) {}
28 };
29 typedef struct pos pos;
30 pos data[MAXN];
31 int n,k,L;
32 pair<double,pair<int,int> > lst[MAXN*MAXN];
33 int fa[MAXN],blocks;
34 /*Function*/
35 void init(){
36     for (int i=0;i<=MAXN;i++)   fa[i]=i;
37     blocks=n;
38 }
39 int find(int x){
40     int a=x,temp;
41     while (x!=fa[x])    x=fa[x];
42     while (a!=fa[a])    temp=fa[a] , fa[a]=x , a=temp;
43     return x;
44 }
45 void join(int x,int y){
46     int fx=find(x),fy=find(y);
47     if (fx!=fy) fa[fx]=fy , blocks--;
48 }
49 inline double getDis(pos a,pos b){
50     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
51 }
52 void readData(){
53     double a,b;
54     scanf("%d%d",&n,&k);
55     for (int i=1;i<=n;i++){
56         scanf("%lf%lf",&a,&b);
57         data[i]=pos(a,b);
58     }
59 }
60 void work(){
61     for (int i=1;i<=n;i++)
62         for (int j=1;j<=n;j++)
63             if (i!=j)   lst[L++]=make_pair(getDis(data[i],data[j]),make_pair(i,j));
64     sort(lst,lst+L);
65     if (blocks<k)  {printf("%.2lf\n",lst[0].first);return;}
66     for (int i=0;i<L;i++){
67         join(lst[i].second.first,lst[i].second.second);
68         if (blocks<k)  {printf("%.2lf\n",lst[i+1].first);break;}
69     }
70 }
71 int main()
72 {
73     readData();
74     init();
75     work();
76     return 0;
77 }
View Code

[Jsoi2010]连通数

  直接缩环这很明显了吧。刚开始以为按拓扑序像DP一样更新就行了,但发现有重复计算。那么就对每个点染色或者求最短路,反正是DAG怎么搞都是O(n)。然后对于每个可达的块把点数乘起来最后累加就行了。总复杂度n^2。网上有人说n^2超时了,那肯定是写搓了。还有那个n^3压位是怎么来的?说是O(n)我还信。。。反正这种方法好写好调。1A。

  UPD:我又想了一下,好像DP是可以的。。压一下状态,记录都是从那几个点过来的,然后按拓扑序转移状态就行了。。这是不是网上说的那种方法?但绝对不是n^3。

  1 /**************************************************************
  2     Problem: 2208
  3     User: wsc500
  4     Language: C++
  5     Result: Accepted
  6     Time:8072 ms
  7     Memory:63836 kb
  8 ****************************************************************/
  9  
 10 #include <cstdio>
 11 #include <cstdlib>
 12 #include <iostream>
 13 #include <cstring>
 14 #include <algorithm>
 15 #include <queue>
 16 #include <stack>
 17 #include <vector>
 18  
 19 using namespace std;
 20 #define MAXN (2000+10)
 21 #define MAXM (2000*2000+10)
 22 #define minner(a,b) ((a)<(b)?(a):(b))
 23  
 24 /*Debug*/
 25  
 26 #define debug
 27  
 28 /*Gloable*/
 29 struct Graph{
 30     int head[MAXN],next[MAXM],e[MAXM],countside;
 31     void buildside(int a,int b){
 32         e[countside]=b;
 33         next[countside]=head[a];
 34         head[a]=countside++;
 35     }
 36     Graph(){
 37         memset(head,0,sizeof(head));
 38         memset(next,0,sizeof(next));
 39         memset(e,0,sizeof(e));
 40         countside=1;
 41     }
 42 }G1,G2;
 43 int n;
 44 int dfs_clock,SCC_id;// for tarjan()
 45 int id[MAXN],lowlink[MAXN],mark[MAXN];// for tarjan()
 46 bool inStack[MAXN];// for tarjan()
 47 stack<int> S; // for tarjan()
 48 bool v[MAXN];// for color()
 49 int ct[MAXN],ans[MAXN];// for ans;
 50 /*Function*/
 51 void tarjan_dfs(int p){
 52     id[p]=lowlink[p]=dfs_clock++;
 53     S.push(p);
 54     inStack[p]=true;
 55     for (int i=G1.head[p];i>0;i=G1.next[i]){
 56         if (id[G1.e[i]]==-1){
 57             tarjan_dfs(G1.e[i]);
 58             lowlink[p]=minner(lowlink[p],lowlink[G1.e[i]]);
 59         }else if(inStack[G1.e[i]]){
 60             lowlink[p]=minner(lowlink[p],id[G1.e[i]]);
 61         }
 62     }
 63     if (lowlink[p]==id[p]){
 64         while (1){
 65             int now=S.top();S.pop();
 66             inStack[now]=false;
 67             mark[now]=SCC_id;
 68             if (now==p) break;
 69         }
 70         SCC_id++;
 71     }
 72 }
 73 void tarjan(){
 74     for (int i=1;i<=n;i++)  id[i]=-1;
 75     memset(inStack,false,sizeof(inStack));
 76     dfs_clock=1;
 77     SCC_id=1;
 78     for (int i=1;i<=n;i++)  if (id[i]==-1)    tarjan_dfs(i);
 79  
 80     for (int i=1;i<=n;i++)  ct[mark[i]]++;
 81 }
 82 void readData(){
 83     char str[MAXN];
 84     scanf("%d",&n);
 85     for (int i=1;i<=n;i++){
 86         scanf("%s",str);
 87         for (int j=0;j<n;j++)   if (str[j]=='1')   G1.buildside(i,j+1);
 88     }
 89 }
 90 void rebuild(){
 91     for (int i=1;i<=n;i++){
 92         for (int j=G1.head[i];j>0;j=G1.next[j])
 93                 if (mark[i]!=mark[G1.e[j]]) {
 94                         G2.buildside(mark[i],mark[G1.e[j]]);
 95                     //cout<<mark[i]<<" "<<mark[G1.e[j]]<<endl;
 96                 }
 97     }
 98     n=SCC_id-1;
 99 }
100 void color(int p){
101     queue<int> q;
102     q.push(p);
103     //v[p]=true;
104     while (!q.empty()){
105         int now=q.front();q.pop();
106         for (int i=G2.head[now];i>0;i=G2.next[i])
107             if (!v[G2.e[i]]) v[G2.e[i]]=true , q.push(G2.e[i]);
108     }
109 }
110 void work(){
111     for (int i=1;i<=n;i++){
112         memset(v,false,sizeof(v));
113         color(i);
114         for (int j=1;j<=n;j++)  if (v[j])
115             ans[j]+=ct[i]*ct[j];
116     }
117 }
118 void printAns(){
119     int result=0;
120     for (int i=1;i<=n;i++)  result+=ans[i] , result+=ct[i]*(ct[i]);
121     printf("%d\n",result);
122 }
123 int main()
124 {
125     readData();
126     tarjan();
127     rebuild();
128     work();
129     printAns();
130     return 0;
131 }
View Code

 [Jsoi2009]瓶子和燃料

  首先那个倒水问题的最小答案是瓶子容量们的最大公约数。原理等同于辗转相减(还是叫更相减损?)。那么问题就转化成了给出n个数,选出k个是他们的GCD最大。进一步转化就是求一个最大的数使他是至少k个数的约数。那么就把每个数因式分解(注意不是质因数分解),然后找到出现次数>=k且大小最大的因数就行了,因为肯定有解,所以直接按出现次数和数字大小排一下序就行了。

 1 /**************************************************************
 2     Problem: 2257
 3     User: wsc500
 4     Language: C++
 5     Result: Accepted
 6     Time:2244 ms
 7     Memory:7132 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <cstdlib>
12 #include <iostream>
13 #include <cstring>
14 #include <algorithm>
15 #include <cmath>
16  
17 using namespace std;
18 #define MAXN (1000001+10)
19 #define MAXF (500000)
20  
21 /*Gloable*/
22 int lst[MAXF],v[MAXN],s;
23 int n,k;
24 /*Function*/
25 void readData(){
26     scanf("%d%d",&n,&k);
27     for (int i=1;i<=n;i++)  scanf("%d",&v[i]);
28 }
29 void getPrimeFact(int x){
30     int i;
31     for (i=1;i*i<x;i++){
32         if (x%i==0) lst[s++]=i , lst[s++]=x/i;
33         //while (x%i!=0)  x/=i;
34     }
35     if (i*i==x) if (x%i==0) lst[s++]=i;
36 }
37 void work(){
38     for (int i=1;i<=n;i++)
39         getPrimeFact(v[i]);
40     sort(lst,lst+s);
41     int st=0,mx=0;
42     lst[s]=-1;
43     for (int i=0;i<s;i++){
44         if (lst[i+1]!=lst[i]){
45             if (i+1-st>=k&&lst[i]>lst[mx])   mx=i;
46             st=i+1;
47         }
48     }
49     printf("%d\n",lst[mx]);
50 }
51 int main()
52 {
53     readData();
54     work();
55     return 0;
56 }
View Code

 

posted @ 2013-06-24 23:04  wsc500  阅读(535)  评论(0编辑  收藏  举报