#14:
T1:f[x]=x-1(x&1)||x/2(x&1=0) 求[n,m]有多少个数可以通过变换得到k。(1e9).
=>好像cf上看过类似的题,用二进制的方式来写。不过我没有考虑111的情况算出来的是110的结果。所以WA了。而且也写的非常复杂。
=>T1难度一般不大应好好的想想。
T2:(n,m)有向图,可有自环和重边,求多少种方案使得从(1,n)至少经过n-1条边。
=>组合数学(线性求逆元或费马小定理求逆元)。我不会隔板法求组合数所以不会。
隔板法:http://wenku.baidu.com/link?url=cnMkafED-eyAX0ghvRIqLKvr7pFY0bqHbMpSO_wM_Ruhg6NKmUzlUmHvwt2LTy4KbizvS1J4H9RUDe0oZXILwsxRicZ-Y1ELhuWPMFS81B7
=>T2要是安静想想应该挺好写的。
T3:给一个2e4*2e4的矩阵,1e5的点可以转向,转向费用为1,单位距离费用为2,求(sx,sy)->(tx,ty)最小费用
=>果断跑60%数据 500*500 的最短路。但是只要用行和列来建图就可以了。
=>T3先想好暴力然后尽量的想想正解吧,感觉其实也不会说太难,只是自己很多东西不会用。
summary:0+0+60=60
#49:
T1:x=x+x,x=x*x,x=x-x,x=x/x。给出(n,m)问至少几步n能到达m。
=>暴力bfs+map。然后写了个暴搜输出一百组数据然后测一测。没问题。后来自己手造了几组数据发现自己一种情况没有考虑。然后A了。
=>T1我不怎么会写对拍那么这样子做也就好了吧。
//%%%zs%%%czl%%%ccz%%%yzm%%%sxt%%%yyl
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<map>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define ll long long
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=1e6+5;
const int inf=0x7f7f7f7f;
int q[nmax];
map<int,int>m;
int main(){
freopen("great.in","r",stdin);
freopen("great.out","w",stdout);
int a=read(),b=read();
if(a==b){
puts("0");return 0;
}
m[a]=0;int l,r,x,t;ll ta,tb;
if(a!=1){
l=1,r=2;q[1]=1;q[2]=a;m[1]=1;
}else l=1,r=1,q[1]=1;
int ans=-1;
while(l<=r){
x=q[l++];t=m[x];
ta=x+x;tb=1ll*x*x;
if(ta<=b&&!m[ta]&&ta!=a) m[ta]=t+1,q[++r]=ta;
if(tb<=b&&!m[tb]&&ta!=a) m[tb]=t+1,q[++r]=tb;
if(ta==b||tb==b) {
ans=t+1;
break;
}
//rep(i,l,r) printf("%d ",q[i]);printf("\n");
}
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
T2:(n,m)的无向图,求(1,n)的路径上去掉k条路后最长的路径最短。
=>明显的二分,然后我傻逼写了MST+重新建图+dijkstra。直接二分+dijkstra就可以了
=>T2难度要相信自己是可以A掉的!!!
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<queue>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define qwq(x) for(edge *o=head[x];o;o=o->next)
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=1e3+5;
const int maxn=2e4+5;
const int inf=0x7f7f7f7f;
struct edge{
int to,dist;edge *next;
};
edge es[maxn],*pt=es,*head[nmax];
void add(int u,int v,int d){
pt->to=v;pt->dist=d;pt->next=head[u];head[u]=pt++;
pt->to=u;pt->dist=d;pt->next=head[v];head[v]=pt++;
}
struct zs{
int x,dist;
zs(int x,int dist):x(x),dist(dist){}
zs(){}
bool operator<(const zs&rhs)const{
return dist>rhs.dist;}
};
priority_queue<zs>q;
int n,m,k,dist[nmax];
bool dijkstra(int x){
clr(dist,0x7f);dist[1]=0;q.push(zs(1,0));
zs oo;
while(!q.empty()){
oo=q.top();q.pop();
if(dist[oo.x]!=oo.dist) continue;
qwq(oo.x) if(dist[o->to]>oo.dist+(o->dist>x)){
dist[o->to]=oo.dist+(o->dist>x);
q.push(zs(o->to,oo.dist+(o->dist>x)));
}
}
return dist[n]<=k;
}
int main(){
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
n=read(),m=read(),k=read();int u,v,d,r=0;
rep(i,1,m) u=read(),v=read(),d=read(),add(u,v,d),r=max(r,d);
int l=0,mid,ans=-1;
while(l<=r){
mid=(l+r)>>1;
if(dijkstra(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
T3:n个字符串,?可以是任意字母。求有多少个字符串满足n个中恰好有k个匹配。(包含小写字母)。匹配的定义是完全重合。字符串的长度都相同。
=>没有看到恰好匹配。非常傻叉的想到了一个很容易推翻的算法然后我就很放心的敲了起来。然后敲完一测自己的数据WA了。正解是状压dp。感觉也是状压dp的套路,但我根本没有往这方面去想!!!。其实这题状压dp可以说是直觉吧。第三题就不要总想这乱搞了。
=>一定要好好想想有没有什么反例有没有什么漏洞!!!
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int inf=0x7f7f7f7f;
const int mod=1e6+3;
int pre[55][30],eo[20];
char s[20][55];
int f[55][40233];
//f[i][j]第i个字符第j种状态的方案数。
void M(int &a){
if(a>=mod) a-=mod;
}
int main(){
freopen("set.in","r",stdin);
freopen("set.out","w",stdout);
int n=read(),k=read();
rep(i,1,n) scanf("%s",s[i]+1);
int len=strlen(s[1]+1);
eo[1]=1;rep(i,2,17) eo[i]=eo[i-1]*2;
rep(i,1,len) rep(j,0,25) rep(k,1,n) if(s[k][i]-'a'==j||s[k][i]=='?') pre[i][j]|=eo[k];
int st=(1<<n)-1;f[0][st]=1;
rep(i,1,len) {
rep(j,0,st) rep(k,0,25){
M(f[i][j&pre[i][k]]+=f[i-1][j]);
}
//rep(j,0,st) printf("%d ",f[i][j]);printf("\n");
}
int ans=0;
rep(i,0,st) {
int tmp=i,cnt=0;while(tmp) cnt+=tmp&1,tmp>>=1;
if(cnt==k) M(ans+=f[len][i]);
//printf("(%d )%d\n",i,tmp);
}
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
summary:100+80+0=180
#15
T1:a[i]>=a[i/2]。输出a的最大字典序
=>可以发现这是二叉树的情况那么就先预处理出每个点有多少个儿子然后递归处理就可以了。
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=1e5+5;
const int inf=0x7f7f7f7f;
int a[nmax],son[nmax],ans[nmax],n;
void dfs(int x,int l,int r){
if(x>n) return ;ans[x]=a[l];
dfs(x*2,r-son[x*2]+1,r);dfs(x*2+1,l+1,l+son[x*2+1]);
}
int main(){
freopen("lazy.in","r",stdin);
freopen("lazy.out","w",stdout);
n=read();rep(i,1,n) a[i]=read();sort(a+1,a+n+1);
dwn(i,n,1) son[i]=son[i*2]+son[i*2+1]+1;
//rep(i,1,n) printf("%d ",son[i]);printf("\n");
dfs(1,1,n);
rep(i,1,n) printf("%d ",ans[i]);printf("\n");
fclose(stdin);
fclose(stdout);
return 0;
}
/*
7
1 2 3 4 5 6 7
6
1 2 3 4 5 6
*/
T2:n:5e4,k=7 k维空间求曼哈顿距离的最远点对。
=>二维的情况下是O(N2K)的显然不行,然后我们可以根据2维的情况推广到k维的情况。复杂度是O(nk2^k)的。
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define ll long long
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*f;
}
const int nmax=5e4+5;
const int maxn=8;
const ll inf=1e17;
int a[nmax][maxn];bool vis[maxn];
int main(){
freopen("escape.in","r",stdin);
freopen("escape.out","w",stdout);
int n=read(),k=read();rep(i,1,n) rep(j,1,k) a[i][j]=read();
int all=(1<<k)-1;ll u,v,d,tmp,temp,cnt,ans=0;
rep(i,0,all){
u=inf;v=-inf;
clr(vis,0);for(int eo=i,et=1;eo;eo>>=1,++et) vis[et]=eo&1;
//printf("%d\n",i);
//rep(j,1,k) printf("%d",vis[j]);printf("\n");
rep(j,1,n){
temp=0;
rep(t,1,k) temp+=vis[t]?a[j][t]:-a[j][t];
u=min(u,temp);v=max(v,temp);
//printf("%d ",temp);
}
//printf("()\n");
ans=max(ans,v-u);
}
printf("%lld\n",ans);
fclose(stdin);fclose(stdout);
return 0;
}
T3:n个数可以取出k个宽度为d的块。求最大值。取出后数的权值为0。
=>完全没有思路。。。正解好像是单调队列+dp?妈呀先留坑吧。。
=>发现一个奇怪的状态就是早操前想题总是可以很快的想出来。。。然后我就发现这道题不是傻叉单调队列和前缀和优化dp么。。。不要总是以为第三题不可做!
=>然后写了个暴力跑了跑造造数据跑了跑。。。嗯交。A了三个点。。。
=>于是我不服。。。想了想可能的原因:代码应该是不会写错的,那么应该就是题看错了。果然我没考虑右边界的情况。。。TAT
=>一定要看清楚题一定要看清楚题一定要看清楚题一定要看清楚题一定要看清楚题一定要看清楚提!
嗯扔代码:
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*f;
}
const int nmax=12330;
const int maxn=505;
const int inf=0x7f7f7f7f;
int dp[maxn][nmax],t[nmax],a[nmax],q[nmax];
int main(){
freopen("bowling.in","r",stdin);
freopen("bowling.out","w",stdout);
int T=read();
rep(_T,1,T){
int n=read(),k=read(),w=read();
rep(i,1,n) a[i]=read()+a[i-1];
rep(i,n+1,n+w) a[i]=a[n];n+=w;
rep(i,1,n) dp[1][i]=a[i]-a[max(i-w,0)];
rep(i,1,n) t[i]=max(t[i-1],dp[1][i]);
rep(i,2,k){
int l=1,r=0;
rep(j,1,n) {
dp[i][j]=-inf;
if(j>=w) dp[i][j]=max(dp[i][j],t[j-w]+a[j]-a[j-w]);
if(l<=r&&q[l]<=j-w) ++l;
while(l<=r&&dp[i-1][j]-a[j]>=dp[i-1][q[r]]-a[q[r]]) --r;
q[++r]=j;
dp[i][j]=max(dp[i][j],dp[i-1][q[l]]+a[j]-a[q[l]]);
}
rep(j,1,n) t[j]=max(t[j-1],dp[i][j]);
}
int ans=-inf;
rep(i,0,n) ans=max(ans,dp[k][i]);
printf("%d\n",ans);
}
fclose(stdin);fclose(stdout);
return 0;
}
#16
T1:给出一个由#和i的n*m的矩阵(n,m=50),初始只有一个#联通块。问最少将几个i变成#可以使得联通块增加。
=>我感觉好像答案肯定是小于3的。然后就O(n4)判断是否可以1.不行的话就输出2
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*f;
}
const int nmax=55;
const int maxn=nmax*nmax;
const int inf=0x7f7f7f7f;
char s[nmax][nmax];int a[nmax][nmax],n,m;
struct node{
int x,y;
node(int x,int y):x(x),y(y){}
node(){}
};
node q[maxn];bool vis[nmax][nmax];
int xx[4]={0,0,1,-1};
int yy[4]={1,-1,0,0};
bool bfs(int x,int y){
a[x][y]=0;clr(vis,0);
rep(i,1,n) rep(j,1,m) if(a[i][j]){
int l=1,r=1;q[1]=node(i,j);vis[i][j]=1;
node oo;int tx,ty;
while(l<=r){
oo=q[l++];
rep(k,0,3) {
tx=oo.x+xx[k];ty=oo.y+yy[k];
if(tx<1||ty<1||tx>n||ty>m) continue;
if(a[tx][ty]&&!vis[tx][ty]){
vis[tx][ty]=1;q[++r]=node(tx,ty);
}
}
}
bool flag=0;
rep(k,1,n) rep(t,1,m) if(a[k][t]&&!vis[k][t]) flag=1;
a[x][y]=1;if(flag) return 1;
return 0;
}
}
int main(){
freopen("divide.in","r",stdin);
freopen("divide.out","w",stdout);
n=read(),m=read();
rep(i,1,n) scanf("%s",s[i]+1);
rep(i,1,n) rep(j,1,m) a[i][j]=s[i][j]=='#';
int cnt=0;rep(i,1,n) rep(j,1,m) cnt+=(a[i][j]);
if(cnt<3) puts("-1");
else if(n==1||m==1) puts("1");
else{
bool flag=0;
rep(i,1,n) rep(j,1,m) if(a[i][j]) if(bfs(i,j)) flag=1;
if(flag) puts("1");
else puts("2");
}
fclose(stdin);fclose(stdout);
return 0;
}
/*
1 5
#...#
#...#
#.###
#...#
#####
*/
T2:给出一个(n,m)的无向图,(n<=200,m<=40000),求有多少个点是关键点,即去掉该点后有点对的mindist被改变。
=>n那么小,每个点都跑一边dijkstra,同时计算最短路径的方案数。然后O(n3)判断是否是关键点即可。好像usaco有计算方案数的我看的hzwer的题解的类似题嘿嘿嘿
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<queue>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define qwq(x) for(edge *o=head[x];o;o=o->next)
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*f;
}
const int nmax=205;
const int maxn=8e4+5;
const int inf=0x7f7f7f7f;
struct edge{
int to,dist;edge *next;
};
edge es[maxn],*pt=es,*head[nmax];
void add(int u,int v,int d){
pt->to=v;pt->dist=d;pt->next=head[u];head[u]=pt++;
pt->to=u;pt->dist=d;pt->next=head[v];head[v]=pt++;
}
struct zs{
int x,dist;
zs(int x,int dist):x(x),dist(dist){}
zs(){}
bool operator<(const zs&rhs)const{
return dist>rhs.dist;}
};
priority_queue<zs>q;
int da[nmax][nmax],ca[nmax][nmax];
void DIJKSTRA(int s){
clr(da[s],0x7f);da[s][s]=0;clr(ca[s],0);
q.push(zs(s,0));zs oo;
while(!q.empty()){
oo=q.top();q.pop();
if(da[s][oo.x]!=oo.dist) continue;
qwq(oo.x) if(da[s][o->to]>oo.dist+o->dist){
da[s][o->to]=oo.dist+o->dist;ca[s][o->to]=1;
q.push(zs(o->to,da[s][o->to]));
}else if(da[s][o->to]==oo.dist+o->dist) ca[s][o->to]++;
}
}
bool vis[nmax];
int main(){
freopen("city.in","r",stdin);
freopen("city.out","w",stdout);
int n=read(),m=read(),u,v,d;clr(da,0x7f);
rep(i,1,m) u=read(),v=read(),d=read(),da[u][v]=da[v][u]=min(da[u][v],d);
rep(i,1,n-1) rep(j,i+1,n) if(i!=j&&da[i][j]!=inf) add(i,j,da[i][j]);
rep(i,1,n) DIJKSTRA(i);
rep(i,1,n-1) {
rep(j,i+1,n) {
//printf("->%d %d\n",i,j);
rep(k,1,n) if(k!=i&&k!=j&&da[i][k]+da[k][j]==da[i][j]&&ca[i][k]*ca[k][j]==ca[i][j]) vis[k]=1;
/*printf("ds:");rep(k,1,n) printf("%d ",da[k]);printf("\n");
printf("dt:");rep(k,1,n) printf("%d ",db[k]);printf("\n");
printf("cs:");rep(k,1,n) printf("%d ",ca[k]);printf("\n");
printf("ct:");rep(k,1,n) printf("%d ",cb[k]);printf("\n");*/
}
}
int ans=0;
rep(i,1,n) if(vis[i]) ++ans;
if(!ans) puts("No important cities.");
else rep(i,1,n) if(vis[i]) printf("%d ",i);printf("\n");
fclose(stdin);fclose(stdout);
return 0;
}
/*
4 4
1 2 1
2 3 1
4 1 2
4 3 2
*/
T3:给出1e5的数组。[l,r]的xor and or值。l,r=[1,n]。求总和/n*n。
=>n*n的算法是显然的。我只会写40分的部分分。
=>然后xor的情况总是处理不出来。然后看了题解发现还是自己想复杂了。都是O(N)递推而已。
=>嗷嗷嗷为什么我这么蠢啊。。。懒得写了。贴一下kpm的代码%%%
#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstring>
#define rep(i, l, r) for(int i=l; i<=r; i++)
#define down(i, l, r) for(int i=l; i>=r; i--)
#define travel(x) for(edge *p=fir[x]; p; p=p->n)
#define clr(x, c) memset(x, c, sizeof(x))
#define maxn 100009
#define ll long long
#define inf 0x3fffffff
using namespace std;
int read()
{
int x=0, f=1; char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
while (isdigit(ch)) x=x*10+ch-'0', ch=getchar();
return x*f;
}
int n, k[maxn];
int S, T, now, tmp; ll sum;
int main()
{
freopen("nine.in", "r", stdin);
freopen("nine.out", "w", stdout);
n=read(); rep(i, 1, n) k[i]=read();
double Xans=0, Aans=0, Oans=0;
for(int a=1; a<inf; a<<=1)
{
sum=0;
S=1, T=0, now=0;
rep(i, 1, n)
{
now^=(k[i]&a)?1:0;
sum+=now?S:T;
now?T++:S++;
}
tmp=0; now=0; S--;
rep(i, 1, n)
{
now^=(k[i]&a)?1:0;
now?T--:S--;
sum+=tmp?S:T;
tmp=now;
}
Xans+=1.0*sum*a;
S=0; sum=0;
rep(i, 1, n)
if (k[i]&a) S++; else sum+=1LL*S*S, S=0;
sum+=1LL*S*S;
Aans+=1.0*sum*a;
S=0; sum=1LL*n*n;
rep(i, 1, n)
if (k[i]&a) sum-=1LL*S*S, S=0; else S++;
sum-=1LL*S*S;
Oans+=1.0*sum*a;
}
Xans/=1.0*n*n;
Aans/=1.0*n*n;
Oans/=1.0*n*n;
printf("%.3lf %.3lf %.3lf\n", Xans, Aans, Oans);
fclose(stdin);
fclose(stdout);
return 0;
}
#17
T1:给出n个数的f[i],表示的是以该点为结尾的最长递增子序列。。
=>完全一脸蒙蔽。。应该只是noipT1的难度啊TAT。ccz看完说了句排个序。。。然后我就想到了。。。
=>其实只是排序后乱搞就可以了。。挺有趣的一道题。
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=1e5+5;
const int inf=0x7f7f7f7f;
struct node{
int x,cur;
node(int x,int cur):x(x),cur(cur){}
node(){}
bool operator<(const node&rhs)const{
return x<rhs.x||x==rhs.x&&cur>rhs.cur;}
};
node ns[nmax];
int ans[nmax];
int main(){
freopen("search.in","r",stdin);
freopen("search.out","w",stdout);
int T=read();
rep(_T,1,T){
int n=read(),mx=0;bool flag=1;
rep(i,1,n) {
ns[i].x=read(),ns[i].cur=i;
if(ns[i].x>mx+1) flag=0;
mx=max(mx,ns[i].x);
}
if(!flag){
puts("No");continue;
}
sort(ns+1,ns+n+1);
rep(i,1,n) ans[ns[i].cur]=i;
rep(i,1,n) printf("%d ",ans[i]);printf("\n");
}
fclose(stdin);fclose(stdout);
return 0;
}
/*
2
5
1 2 2 3 1
4
1 3 2 3
*/
T2:给出平面内n个点。求一颗树,树中有边相连的两个节点的权值和除以(树边和-该边);求最大值。
=>想啊想想啊想然后被最小生成树死可。。。然后想着从AB两个点入手,那么复杂度是O(N3logn)的。然后可以发现最小生成树有很多计算重复的地方,那么可以求最小生成树后logn的时间求出每个点对的最小生成树。
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<cmath>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define qwq(x) for(edge *o=head[x];o;o=o->next)
#define lson l,mid,x<<1
#define rson mid+1,r,x<<1|1
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=2e3+5;
const int maxn=1e6+5;
const int inf=0x7f7f7f7f;
int a[nmax],x[nmax],y[nmax];
struct zs{
int u,v;double d;
zs(int u,int v,double d):u(u),v(v),d(d){}
zs(){}
bool operator<(const zs&rhs)const{
return d<rhs.d;}
};
zs zl[maxn];
int fa[nmax],n;
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
struct edge{
int to;double dist;edge *next;
};
edge es[nmax<<1],*pt=es,*head[nmax];
void add(int u,int v,double d){
pt->to=v;pt->dist=d;pt->next=head[u];head[u]=pt++;
pt->to=u;pt->dist=d;pt->next=head[v];head[v]=pt++;
}
double gd(int a,int b){
return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
int sz[nmax],dep[nmax],tp[nmax],id[nmax],idx[nmax],son[nmax];double v[nmax];
void dfs(int x,int f){
sz[x]=1;
qwq(x) if(o->to!=f) {
v[o->to]=o->dist;fa[o->to]=x;dep[o->to]=dep[x]+1;
dfs(o->to,x);sz[x]+=sz[o->to];
if(!son[x]||sz[son[x]]<sz[o->to]) son[x]=o->to;
}
}
void DFS(int x,int top){
tp[x]=top;id[++id[0]]=x;idx[x]=id[0];
if(son[x]) DFS(son[x],top);
qwq(x) if(!idx[o->to]) DFS(o->to,o->to);
}
double mx[nmax<<2];
void build(int l,int r,int x){
if(l==r) mx[x]=v[id[l]];
else{
int mid=(l+r)>>1;build(lson);build(rson);
mx[x]=max(mx[x<<1],mx[x<<1|1]);
}
}
double query(int tl,int tr,int l,int r,int x){
if(tl<=l&&tr>=r) return mx[x];
int mid=(l+r)>>1;double ans=0;
if(tl<=mid) ans=max(ans,query(tl,tr,lson));
if(tr>mid) ans=max(ans,query(tl,tr,rson));
return ans;
}
double qmax(int a,int b){
//printf("%d %d\n",a,b);
double ans=0;
while(tp[a]!=tp[b]){
if(dep[tp[a]]>dep[tp[b]]) swap(a,b);
ans=max(ans,query(idx[tp[b]],idx[b],1,n,1));b=fa[tp[b]];
}
if(dep[a]>dep[b]) swap(a,b);
//printf("->%d %d\n",a,b);
if(idx[a]!=idx[b]) ans=max(ans,query(idx[a]+1,idx[b],1,n,1));
return ans;
}
void print(int l,int r,int x){
printf("%d %d %lf\n",l,r,mx[x]);
if(l==r) return ;
int mid=(l+r)>>1;print(lson);print(rson);
}
void build(){
fa[1]=0;dfs(1,0);DFS(1,1);build(1,n,1);//print(1,n,1);
//rep(i,1,n) printf("%d %d %d %d %d %d %lf\n",fa[i],sz[i],son[i],dep[i],tp[i],id[i],v[i]);
double ans=0,sm=0;rep(i,1,n) sm+=v[i];
/*printf("=>%lf\n",qmax(2,4));
printf("==>%lf\n",query(2,2,1,n,1));*/
rep(i,1,n-1) rep(j,i+1,n) {
ans=max(ans,(a[i]+a[j])*1.0/(sm-qmax(i,j)));
}
printf("%.2lf\n",ans);
}
int main(){
freopen("loser.in","r",stdin);
freopen("loser.out","w",stdout);
n=read();
rep(i,1,n) x[i]=read(),y[i]=read(),a[i]=read();
int cnt=0;rep(i,1,n-1) rep(j,i+1,n) zl[++cnt]=zs(i,j,gd(i,j));
sort(zl+1,zl+cnt+1);
rep(i,1,n) fa[i]=i;
int tm=0,ta,tb;rep(i,1,cnt){
ta=find(zl[i].u);tb=find(zl[i].v);
if(ta!=tb){
fa[ta]=tb;
add(zl[i].u,zl[i].v,zl[i].d);
if(++tm==n-1) break;
}
}
/*rep(i,1,n){
qwq(i) printf("(%d %d)=%lf\n",i,o->to,o->dist);
}*/
build();
fclose(stdin);fclose(stdout);
}
/*
4
1 1 20
1 2 30
200 2 80
200 1 100
*/
T3:给出n个数1/0,1表示该数应该比前一个数大,0表示该数应该比前一个数小,数的范围是[1,n],每个数只能用一次。
=>我只会20分的O(2N)的暴力。。。正解是dp完全不会T_T