[SCOI2005]繁忙的都市

61.187.179.132:8080/JudgeOnline/showproblem

[SCOI2005]繁忙的都市

Time Limit:10000MS  Memory Limit:165536K
Total Submit:20 Accepted:16
Case Time Limit:1000MS

Description

城市C是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造。城市C的道路是这样分布的:城市中有n个交叉路口, 有些交叉路口之间有道路相连,两个交叉路口之间最多有一条道路相连接。这些道路是双向的,且把所有的交叉路口直接或间接的连接起来了。每条道路都有一个分 值,分值越小表示这个道路越繁忙,越需要进行改造。但是市政府的资金有限,市长希望进行改造的道路越少越好,于是他提出下面的要求:
1. 改造的那些道路能够把所有的交叉路口直接或间接的连通起来。
2. 在满足要求1的情况下,改造的道路尽量少。
3. 在满足要求1、2的情况下,改造的那些道路中分值最大的道路分值尽量小。
任务:作为市规划局的你,应当作出最佳的决策,选择那些道路应当被修建。

Input

第一行有两个整数n,m表示城市有n个交叉路口,m条道路。接下来m行是对每条道路的描述,u, v, c表示交叉路口u和v之间有道路相连,分值为c。(1≤n≤300,1≤c≤10000)

Output

两个整数s, max,表示你选出了几条道路,分值最大的那条道路的分值是多少。

Sample Input

4 5
1 2 3
1 4 5
2 4 7
2 3 6
3 4 8

Sample Output

3 6

Source

水题!边排序之后直接并查集
代码最短速度第二(*^__^*)
Code:
#include<cstdio>
#include<algorithm>
#include<vector>
#define rep(i,n) for(int i=0;i<n;i++)
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef pair<int,int> ii;
typedef pair<int,ii> edge;
vector<edge> E;
vector<int> F;
int find(int x){return F[x]==x?x:(F[x]=find(F[x]));}
int main()
{
int n,m,s,t,c,ans;
scanf("%d %d",&n,&m);
rep(i,n) F.pb(i);
rep(i,m)scanf("%d %d %d",&s,&t,&c),E.pb(edge(c,ii(s-1,t-1)));
sort(all(E));
rep(i,m)
{
int a=find(E[i].second.first),b=find(E[i].second.second);
if(a!=b) F[a]=b,ans=E[i].first;
}
printf("%d %dn",n-1,ans);
}

[JSOI2008]星球大战starwar

61.187.179.132:8080/JudgeOnline/showproblem

[JSOI2008]星球大战starwar

Time Limit:3000MS  Memory Limit:165536K
Total Submit:90 Accepted:43
Case Time Limit:1000MS

Description

很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系。某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星 系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。
但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通 讯通道也开始不可靠起来。现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打 击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。

Input

输入文件第一行包含两个整数,N (1 <= N <= 2M) 和M (1 <= M <= 200,000),分别表示星球的数目和以太隧道的数目。星球用0~N-1的整数编号。
接下来的M行,每行包括两个整数X, Y,其中(0<=X<>Y

Output

输出文件的第一行是开始时星球的连通块个数。
接下来的N行,每行一个整数,表示经过该次打击后现存星球的连通块个数。

Sample Input

8 13
0 1
1 6
6 5
5 0
0 6
1 2
2 3
3 4
4 5
7 1
7 2
7 6
3 6
5
1
6
3
5
7

Sample Output

1
1
1
2
3
3

Source

额。算法鬼都知道(*^__^*) 就是倒过来然后加点然后用并查集。这没话说。。
关键是如果按我一般的方式用vector表示边的话会TLE囧。。
只好用前向星了。惊讶的发现前向星可以写的怎么短!以后遇到这种情况都用前向星了(*^__^*)
Code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<utility>
#define rep(i,n) for(int i=0;i<n;i++)
#define For(i,a,b) for(int i=a;i<=b;i++)
#define tr(i,x) for(vi::iterator i=x.begin();i!=x.end();i++)
#define all(x) x.begin(),x.end()
#define pb push_back
#define divide cout<<"—————"<<endl;
using namespace std;
const int inf=~0U>>1,maxm=400000,maxn=maxm;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef pair<int,int> ii;
ii E[maxm];
int A[maxn],Ans[maxn],S[maxn+1]={0},T[maxm];
bool inSet[maxn]={0},used[maxn]={0};
int F[maxn],n,m,cnt;
void makeset(int x)
{
F[x]=x;cnt++;inSet[x]=true;
}
int find(int x)
{
if(F[x]==x)return x;
return F[x]=find(F[x]);
}
void Union(int i,int j)
{
if(!inSet[i]||!inSet[j])return;
i=find(i);j=find(j);
if(i!=j) F[i]=j,cnt–;
}
int main()
{
//freopen("in","r",stdin);
scanf("%d %d",&n,&m);int s,t;cnt=0;
rep(i,m)
{
scanf("%d %d",&s,&t);
E[i*2]=ii(s,t);
E[i*2+1]=ii(t,s);
S[s]++;S[t]++;
}
for(int i=1;i<=n;i++) S[i]+=S[i-1];
for(int i=2*m-1;i>=0;–i)
T[–S[E[i].first]]=E[i].second;
int k;scanf("%d",&k);
rep(i,k)scanf("%d",A+i),used[A[i]]=true;
rep(i,n)if(!used[i])makeset(i);
rep(i,n)if(!used[i])for(int e=S[i];e<S[i+1];e++)Union(i,T[e]);
Ans[k]=cnt;
for(int i=k-1;i>=0;–i)
{
int t=A[i];makeset(t);
for(int e=S[t];e<S[t+1];e++)Union(t,T[e]);
Ans[i]=cnt;
}
rep(i,k+1)printf("%dn",Ans[i]);
}

[SCOI2009]最长距离

[SCOI2009]最长距离

Time Limit:10000MS  Memory Limit:165536K
Total Submit:25 Accepted:16
Case Time Limit:1000MS

Description

windy有一块矩形土地,被分为 N*M 块 1*1 的小格子。
有的格子含有障碍物。
如果从格子A可以走到格子B,那么两个格子的距离就为两个格子中心的欧几里德距离。
如果从格子A不可以走到格子B,就没有距离。
如果格子X和格子Y有公共边,并且X和Y均不含有障碍物,就可以从X走到Y。
如果windy可以移走T块障碍物,求所有格子间的最大距离。
保证移走T块障碍物以后,至少有一个格子不含有障碍物。

Input

输入文件maxlength.in第一行包含三个整数,N M T。
接下来有N行,每行一个长度为M的字符串,’0’表示空格子,’1’表示该格子含有障碍物。

Output

输出文件maxlength.out包含一个浮点数,保留6位小数。

Sample Input

【输入样例一】
3 3 0
001
001
110

【输入样例二】
4 3 0
001
001
011
000

【输入样例三】
3 3 1
001
001
001

Sample Output

【输出样例一】
1.414214

【输出样例二】
3.605551

【输出样例三】
2.828427

Hint

20%的数据,满足 1 <= N,M <= 30 ; 0 <= T <= 0 。
40%的数据,满足 1 <= N,M <= 30 ; 0 <= T <= 2 。
100%的数据,满足 1 <= N,M <= 30 ; 0 <= T <= 30 。

Source

Day2
61.187.179.132:8080/JudgeOnline/showproblem
效仿tclsm神牛直接贴题目了。。感觉给我本来就很短的题解凑了点字囧。。
这道题目的感觉是SCOI2009最水的,枚举每个起点,直接BFS,复杂度是O(n^2m^2)的,同时可以规定终点的横坐标大于起点。。减少一半搜索量。。
BFS的时候注意边权可以是1也可以是0,那么用一个deque代替queue,遇到0边就放进对首,否则放进队尾就OK了。。
我从qzc神牛的代码中看到define是可以放在函数中的,而且可以用#undef给取消掉,太orz了。。
Code:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#include<deque>
#include<cmath>
#define rep(i,n) for(int i=0;i<n;i++)
#define pb push_back
#define pf push_front
using namespace std;
typedef pair<int,int> ii;
const int inf=~0U>>1,maxn=30;
int n,m,T;
double ans=0;
bool Map[maxn][maxn];
const int di[]={1,0,-1,0},dj[]={0,1,0,-1};
void bfs(int x,int y)
{
static bool vis[maxn][maxn];
static int dist[maxn][maxn];
memset(vis,0,sizeof(vis));
deque<ii> Q;dist[x][y]=Map[x][y];
vis[x][y]=true;Q.pb(ii(x,y));
#define legal(i,j) (i>=x&&i<n&&j>=0&&j<m&&!vis[i][j])
#define A first
#define B second
double tx,ty;
while(Q.size())
{
ii t=Q.front();int cost=dist[t.A][t.B],xx,yy;Q.pop_front();
if(cost>T)break;
tx=t.A-x;ty=t.B-y;
ans=max(ans,tx*tx+ty*ty);
rep(d,4)
{
xx=t.A+di[d];
yy=t.B+dj[d];
if(legal(xx,yy))
{
if(Map[xx][yy])
Q.pb(ii(xx,yy)),dist[xx][yy]=cost+1;
else
Q.pf(ii(xx,yy)),dist[xx][yy]=cost;
vis[xx][yy]=true;
}
}
}
#undef legal
#undef A
#undef B
}
int main()
{
//freopen("in","r",stdin);
cin>>n>>m>>T;char x;
rep(i,n)rep(j,m)cin>>x,Map[i][j]=x==’1′;
rep(i,n)rep(j,m)bfs(i,j);
ans=sqrt(ans);
printf("%0.6lf",ans);
}

[SCOI2009]迷路

[SCOI2009]迷路

Time Limit:10000MS  Memory Limit:165536K
Total Submit:40 Accepted:24
Case Time Limit:1000MS

Description

windy在有向图中迷路了。
该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1。
现在给出该有向图,你能告诉windy总共有多少种不同的路径吗?
注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

Input

第一行包含两个整数,N T。
接下来有 N 行,每行一个长度为 N 的字符串。
第i行第j列为’0’表示从节点i到节点j没有边。
为’1’到’9’表示从节点i到节点j需要耗费的时间。

Output

包含一个整数,可能的路径数,这个数可能很大,只需输出这个数除以2009的余数。

Sample Input

【输入样例一】
2 2
11
00

【输入样例二】
5 30
12045
07105
47805
12024
12345

Sample Output

【输出样例一】
1

【样例解释一】
0->0->1

【输出样例二】
852

Hint

30%的数据,满足 2 <= N <= 5 ; 1 <= T <= 30 。
100%的数据,满足 2 <= N <= 10 ; 1 <= T <= 1000000000 。

Source

Day2

我擦啊!61.187.179.132:8080/JudgeOnline/showproblem
我一开始没想出来怎么做,很明显要用矩阵乘法,所以我就想怎么搞。。
我想了半天没有什么头绪,感觉这个长度很麻烦,感觉矩阵乘法就是从一个推出下一个,推不出下面很多个额。。很头疼。
过了一天发现由于长度最长是9。。。所以把每9个打包起来就再递推就OK了。。
可是我:

晕死了。。那几个WA是我故意写return 0查出错点的。。
最后发现原来因为矩阵有点大,导致如果我直接在乘法里写临时变量的话会MLE。。
靠靠靠。。我本来就没什么AC率的。。现在悲剧大发了。。
Code:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#define rep(i,n) for(int i=0;i<n;i++)
#define pb push_back
using namespace std;
const int maxt=9,maxv=90,maxn=10,mod=2009;
int n,v;
inline int node(int time,int pos)
{
return time*n+pos;
}
int tmp[maxv][maxv];
struct Mat
{
int M[maxv][maxv];
int&operator()(int i,int j){return M[i][j];}
Mat(){memset(M,0,sizeof(M));}
void operator*=(Mat&o)
{
memset(tmp,0,sizeof(tmp));
rep(i,v)rep(j,v)rep(k,v)
tmp[i][j]+=M[i][k]*o(k,j),tmp[i][j]%=mod;
memcpy(M,tmp,sizeof(M));
}
void operator=(const Mat&o)
{
memcpy(M,o.M,sizeof(M));
}
}orig;
int Map[maxn][maxn];
void Power(int n,Mat&ret)
{
if(n==1){ret=orig;return;}
Power(n/2,ret);
ret*=ret;
if(n&1)ret*=orig;
}
int main()
{
//freopen("in","r",stdin);
//freopen("road.in","r",stdin);
//freopen("road.out","w",stdout);
int T;
scanf("%d %d",&n,&T);char x;v=n*9;
rep(i,n)
{
scanf("n");
rep(j,n)scanf("%c",&x),Map[i][j]=x-‘0’;
}
rep(i,n)for(int t=0;t<8;t++)
orig(node(t,i),node(t+1,i))++;
rep(i,n)rep(j,n)if(Map[j][i])
orig(node(8,i),node(9-Map[j][i],j))++;
Mat Ans;Power(T,Ans);
int dp[maxn][maxt]={0};
dp[0][0]=1;
for(int t=1;t<maxt;t++)
{
rep(i,n)rep(j,n)
if(Map[j][i])
if(t-Map[j][i]>=0)
{
dp[i][t]+=dp[j][t-Map[j][i]];
dp[i][t]%=mod;
}
}
int ans=0;
rep(i,n)rep(t,maxt)
{
ans+=Ans(node(0,n-1),node(t,i))*dp[i][t];
ans%=mod;
}
cout<<ans<<endl;
}

Vijos 香樟树

靠。。刚想出这道题Vijos居然奔溃了。。我哭啊。。
题目是说给你n个数,都小于10^5,然后让你求出其中一个子序列(连不连续无所谓,只要顺序是原序列的顺序就OK)。。相邻两个数不互质。。让你求这个序列的最大长度。。n<=100000
很明显暴力DP是会悲剧的,如果你用Dpi表示第i个数结尾的最长长度然后O(n)枚举前一个的话是O(n^2)的。关键是利用题目的性质。
注意到不互质的数一定有一个公共的质因子,就简单了。首先算出10^5一下每个质数,然后开个数组保存每个当前以第i个质数的倍数结尾的数列的最长值,然后对于每个数枚举他的每个质因子计算并更新一下就可以了。。
关键是一个数的质因子数目显然是O(logn)级别的,并且预先处理也可以大大降低枚举量。。程序还是很快的。。
不过VJ挂了。没法测了囧。。

PKU 1743 Muscial Theme

这是一道很经典的题目了。。题目在:http://acm.pku.edu.cn/JudgeOnline/problem?id=1743
USACO上面的那次我是用DP过的,现在不能DP了。我学了一下SA。过掉了。不过又想到了一个用Hash的做法比较NB。。
首先把原系列换成差系列。。
方法还是二分,因为长度为l的重复字串存在那么l-1的肯定也存在,所以问题变成判断所有长度为l的字串中有没有相同且不相交的,这可以用Hash来解决,算出每个字串的Hash值然后用一个Hash表来保存,有点类似Rabin-Karp算法。。然后二分就OK了。。为了调试我还去USACO了一下。。好久没去了。有点怀念饿。。
Code:

/* PROG: theme LANG: C++ ID: Tom Chen*/#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<cstdlib>#include<cstring>#include<string>#include<vector>#include<set>#include<queue>#include<map>#define rep(i,n) for(int i=0;i<n;i++)#define For(i,a,b) for(int i=a;i<=b;i++)#define tr(i,x) for(typeof(x.begin()) i=x.begin();i!=x.end();i++)#define all(x) x.begin(),x.end()#define pb push_backusing namespace std;const int inf=~0U>>1;typedef unsigned int uint;typedef vector<int> vi;typedef vector<vi> vvi;typedef pair<int,int> ii;const int maxn=20000,seed[]={131,1331,10007,133331},size=17771;int A[maxn],n,Len;struct Hash{ struct node { node*next; uint h[4]; int Max,Min; void Renew(int x) { Max=max(Max,x); Min=min(Min,x); } bool Legal() { return Max-Min>Len; } bool equals(uint _h[4])const { for(int i=0;i<4;i++) if(h[i]!=_h[i])return false; return true; } }Data[maxn],*Now,*H[size]; void Clear() { Now=Data; rep(i,size) H[i]=0; } node* new_node(uint h[4],int start,node*next) { memcpy(Now->h,h,sizeof(Now->h)); Now->Max=Now->Min=start; Now->next=next; return Now++; } bool Insert(uint h[4],int start) { int t=h[0]%size; for(node*i=H[t];i;i=i->next) if(i->equals(h)) { i->Renew(start); return i->Legal(); } H[t]=new_node(h,start,H[t]); return false; }}H;bool Check(int l){ H.Clear(); Len=l;uint e[4]={1,1,1,1}; rep(i,4)rep(j,l-1)e[i]*=seed[i]; uint h[4]={0}; rep(i,l)rep(j,4) h[j]*=seed[j],h[j]+=A[i]; H.Insert(h,0); for(int i=1;i+l<=n;i++) { rep(j,4) { h[j]-=A[i-1]*e[j]; h[j]*=seed[j]; h[j]+=A[i+l-1]; } if(H.Insert(h,i)) return true; } return false;}bool Init(){ cin>>n;if(!n)return false;rep(i,n)scanf("%d",A+i); rep(i,n-1) A[i]=A[i+1]-A[i]+90; –n; return true;}void Work(){ int l=0,r=n/2+2; while(l+1<r) { int m=(l+r)/2; if(Check(m))l=m; else r=m; } ++l; if(l<5)cout<<0<<endl; else cout<<l<<endl;}bool Solve(){ if(!Init())return false; Work(); return true;}int main(){ //freopen("in","r",stdin); //freopen("theme.in","r",stdin); //freopen("theme.out","w",stdout); while(Solve());}

VIJOS 1626 爱在心中

这道题目很显然是强连通的题目,用Tarjan算法求出强连通分量之后,要注意一些细节,
单个人不算爱心天使!靠靠靠。。所以强连通分量要特判的。
很显然如果入度为0的强连通分量有2个或以上,就不行了。
而且如果这个分量是一个人的话,还是-1囧。。我基本靠STL。。真爽饿(*^__^*)
幸好样例很强大,1AC了(*^__^*)
Code:

#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<cstdlib>#include<cstring>#include<string>#include<vector>#include<set>#include<queue>#include<map>#define rep(i,n) for(int i=0;i<n;i++)#define For(i,a,b) for(int i=a;i<=b;i++)#define tr(i,x) for(typeof(x.begin()) i=x.begin();i!=x.end();i++)#define all(x) x.begin(),x.end()#define pb push_backusing namespace std;typedef vector<int> vi;typedef vector<vi> vvi;typedef pair<int,int> ii;const int inf=~0U>>1,maxn=1000;int n,m;vvi E;vi ord,low,inStack,Stack,id;int cnt=0,snt=0;int dfs(int x){ ord[x]=low[x]=++cnt; inStack[x]=true;Stack.pb(x); tr(i,E[x]) if(!ord[*i]) low[x]=min(low[x],dfs(*i)); else if(inStack[*i]) low[x]=min(low[x],ord[*i]); if(ord[x]==low[x]) { int u;do{u=Stack.back();Stack.pop_back();id[u]=snt;inStack[u]=false;}while(u!=x); snt++; } return low[x];}void Init(){ cin>>n>>m;int s,t; E.resize(n); while(m–) { scanf("%d %d",&s,&t); E[t-1].pb(s-1); }}void Work(){ vi In,Num; ord=low=inStack=id=vi(n,0); rep(i,n)if(!ord[i])dfs(i); In=Num=vi(snt,0); rep(i,n)tr(e,E[i]) if(id[i]!=id[*e]) In[id[*e]]++; rep(i,n) Num[id[i]]++; int t=0; rep(i,snt)if(Num[i]>1) ++t; cout<<t<<endl; if(count(all(In),0)>1) cout<<-1<<endl; else { int ans=find(all(In),0)-In.begin(); if(Num[ans]==1){cout<<-1<<endl;return;} rep(i,n)if(id[i]==ans)cout<<i+1<<" "; cout<<endl; }}int main(){ //freopen("in","r",stdin); Init(); Work();}

Marco头文件

为了方便编程,我打了个Marco头文件,以后就方便了。。
Code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<map>
#define rep(i,n) for(int i=0;i<n;i++)
#define For(i,a,b) for(int i=a;i<=b;i++)
#define tr(i,x) for(typeof(x.begin()) i=x.begin();i!=x.end();i++)
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
const int inf=~0U>>1;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef pair<int,int> ii;

VIJOS 1070

这个是次小生成树,我用的是n^2的算法,先算出最小生成树,然后算出每一条路径中最大的边,然后枚举每一条不再树上的边。。
代码写的巨丑。。悲剧囧。
Code:
#include<cstdio>#include<vector>#include<algorithm>#include<iostream>#define all(x) x.begin(),x.end()using namespace std;typedef pair<int,int> ii;typedef pair<int,ii> Edge;typedef vector<ii> vii;typedef vii::iterator vit;typedef vector<vii> vvii;vector<Edge> E,NotOnTree;vector<int> F;const int maxn=500;int n,m;void setF(){ for(int i=0;i<n;i++)F.push_back(i);}int Find(int x){ if(x==F[x]) return x; return F[x]=Find(F[x]);}void Union(int a,int b){ F[b]=a;}void Init(){ scanf("%d %d",&n,&m);int s,t,c; while(m–) { scanf("%d %d %d",&s,&t,&c);–s;–t; E.push_back(Edge(c,ii(s,t))); }}vii edge[maxn];int Total=0;void Ins_Edge(int s,int t,int c){ edge[s].push_back(ii(t,c)); edge[t].push_back(ii(s,c));}void dealEdge(const Edge&e){ ii t=e.second; int i=Find(t.first),j=Find(t.second); if(i!=j) { Union(i,j); Ins_Edge(t.first,t.second,e.first); Total+=e.first; } else { NotOnTree.push_back(e); }}void CalMST(){ sort(all(E)); setF(); for_each(all(E),dealEdge);}int Max_Edge[maxn][maxn];int s;void dfs(int x,int f,int Max){ Max_Edge[s][x]=Max; for(vit e=edge[x].begin();e!=edge[x].end();++e) if(e->first!=f) dfs(e->first,x,max(Max,e->second));}int Ans;void dealEdge2(const Edge&e){ int Now=Total-Max_Edge[e.second.first][e.second.second]+e.first; //cout<<e.second.first<<" "<<e.second.second<<" "<<e.first<<endl; //cout<<Now<<endl; if(Ans==-1||Now<Ans) Ans=Now;}void Work(){ CalMST();Ans=-1; for(int i=0;i<n;i++)s=i,dfs(i,-1,0); for_each(all(NotOnTree),dealEdge2); cout<<"Cost:"<<Total<<endl; cout<<"Cost:"<<Ans<<endl;}int main(){ //freopen("in","r",stdin); Init(); Work();}