【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 }
[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 }
[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; }
[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 }
[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 }
[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 }