2017中國大學生程式設計競賽-哈爾濱站
大力施工......(10/13)剩下的三個題好像不太能寫啊?
A.Palindrome(hdu6230)
首先用Manacher處理出所有的迴文半徑,然後可以得到每個點覆蓋的範圍,問題轉換成了,有多少對點,滿足,i能覆蓋j而且j也能覆蓋i,我們按照迴文半徑從大到小進行排序,然後用線段樹進行維護,然後每次查詢當前點能覆蓋的範圍有多少點即可,更新答案,最後將該點更新到線段樹上。
程式碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=5e5+5; char Ma[MAXN]; int Mp[MAXN]; void Manacher(int len) { int l=len; Ma[0]='$'; Ma[l]=0; int mx=0,id=0; for(int i=0;i<l;i++) { Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1; while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++; if(i+Mp[i]>mx) { mx=i+Mp[i]; id=i; } } } struct node { int id,r; node(int _id=0,int _r=0):id(_id),r(_r){} bool operator <(const node &a)const { return r>a.r; } }sv[MAXN]; struct seg { #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 int sum[MAXN<<2]; inline void push_up(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build(int l,int r,int rt) { sum[rt]=0; if(l==r) return ; int mid=(l+r)>>1; build(lson); build(rson); } void update(int pos,int val,int l,int r,int rt) { if(l==r) { sum[rt]=val; return ; } int mid=(l+r)>>1; if(pos<=mid) update(pos,val,lson); if(pos>mid) update(pos,val,rson); push_up(rt); } int query(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R) return sum[rt]; int ret=0; int mid=(l+r)>>1; if(L<=mid) ret+=query(L,R,lson); if(mid<R) ret+=query(L,R,rson); return ret; } }se; void solve() { int len; ll ans=0; scanf("%s",Ma+1); len=strlen(Ma+1); Manacher(len+1); for(int i=1;i<=len;i++) { sv[i]=node(i,Mp[i]-1); } sort(sv+1,sv+1+len); se.build(1,len,1); for(int i=1;i<=len;i++) { int id=sv[i].id,r=sv[i].r; ans+=se.query(max(1,id-r),min(len,id+r),1,len,1); se.update(id,1,1,len,1); } printf("%lld\n",ans); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; scanf("%d",&T); while(T--) { solve(); } return 0; }
B.K-th Number(hdu6231)
二分答案,統計有多少個區間的第k大的值大於等於我們二分的答案,如果區間數大於m,就提高下界繼續二分,否則降低上界。計數的時候可以採用雙指標。
程式碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=1e5+5; int a[MAXN],HASH[MAXN],tot,n,k; ll m; vector<int> sv; int getid(int x) { int ret=lower_bound(HASH+1,HASH+1+tot,x)-HASH; return ret; } bool judge(int mid) { ll ret=0; sv.clear(); sv.push_back(0); for(int i=1;i<=n;i++) { if(a[i]>=mid) { sv.push_back(i); } } for(int i=0;i<sv.size();i++) { if(i<k) continue; ret+=(sv[i-k+1]-sv[i-k])*(n-sv[i]+1); } return ret>=m; } void solve() { tot=0; scanf("%d%d%lld",&n,&k,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); HASH[++tot]=a[i]; } sort(HASH+1,HASH+1+tot); tot=unique(HASH+1,HASH+1+tot)-HASH-1; for(int i=1;i<=n;i++) { a[i]=getid(a[i]); } int l=1,r=tot; int ans=0; while(l<=r) { int mid=(l+r)>>1; if(judge(mid)) { l=mid+1; ans=mid; } else { r=mid-1; } } printf("%d\n",HASH[ans]); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; scanf("%d",&T); while(T--) { solve(); } return 0; }
C.Confliction(hdu6232)
將運動轉換為B對於A的相對運動,然後答案就是經過次數最多的那個點,然後離散化時間和座標,進行差分求解出現的次數最多的那個點就好了,不過挺難寫的啊?
程式碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=2e5+5; int Na,Nb,Ac[MAXN],Bc[MAXN]; ll At[MAXN],Bt[MAXN],Hash[MAXN*2],sz; vector<tuple<ll,int,ll> > seg; void solve() { ll pos=0; seg.clear(); scanf("%d",&Na); for(int i=1;i<=Na;i++) { scanf("%d%lld",&Ac[i],&At[i]); if(At[i]==0) i--,Na--; } scanf("%d",&Nb); for(int i=1;i<=Nb;i++) { scanf("%d%lld",&Bc[i],&Bt[i]); if(Bt[i]==0) i--,Nb--; } sz=0; for(int i=1;i<=Na;i++) { At[i]+=At[i-1];Hash[++sz]=At[i]; } for(int i=1;i<=Nb;i++) { Bt[i]+=Bt[i-1];Hash[++sz]=Bt[i]; } sort(Hash+1,Hash+1+sz); sz=unique(Hash+1,Hash+1+sz)-Hash-1; seg.push_back(make_tuple(0,3,1)); seg.push_back(make_tuple(1,3,-1)); for(int i=1,pa=1,pb=1;i<=sz;i++) { while(At[pa]<Hash[i]) pa++; while(Bt[pb]<Hash[i]) pb++; ll t=Hash[i]-Hash[i-1]; ll delta=Bc[pb]-Ac[pa]; if(delta==0) { seg.push_back(make_tuple(pos,3,t)); seg.push_back(make_tuple(pos+1,3,-t)); } else { ll l=pos+delta,r=pos+t*delta; if(l>r) swap(l,r); int k=3; if(delta==2||delta==-2) k=(l&1)?1:2; seg.push_back(make_tuple(l,k,1)); seg.push_back(make_tuple(r+1,k,-1)); } pos+=t*delta; } sort(seg.begin(),seg.end()); ll mx=0,cnt0=0,cnt1=0; for(int i=0,j=0;i<seg.size();i=j) { while(j<seg.size()&&get<0>(seg[j])==get<0>(seg[i])) { if(get<1>(seg[j])&1) cnt1+=get<2>(seg[j]); if(get<1>(seg[j])&2) cnt0+=get<2>(seg[j]); j++; } if(get<0>(seg[i])&1) mx=max(mx,cnt1); else mx=max(mx,cnt0); } printf("%lld\n",mx); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; scanf("%d",&T); while(T--) { solve(); } return 0; }
D.X-Men(hdu6233)
這可能是本場比賽第二簡單的題目???現場榜被帶歪了啊???其實理性分析一下,可以發現,每次最遠的兩個人的距離一定會減少2,所以直接直徑除二向下取整數就好了。
程式碼:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1005;
struct edge
{
int to,nxt;
}E[MAXN*2];
int head[MAXN],tot,root,mx;
bool book[MAXN];
void addedge(int u,int v)
{
E[tot].to=v;E[tot].nxt=head[u];head[u]=tot++;
E[tot].to=u;E[tot].nxt=head[v];head[v]=tot++;
}
void init(int n)
{
tot=0;
memset(book,0,sizeof(book));
for(int i=1;i<=n;i++)
{
head[i]=-1;
}
}
void dfs(int now,int fa,int dp)
{
if(book[now]&&dp>mx)
{
mx=dp;
root=now;
}
for(int i=head[now];~i;i=E[i].nxt)
{
int v=E[i].to;
if(v==fa)
continue;
dfs(v,now,dp+1);
}
}
void solve()
{
int n,m;
scanf("%d%d",&n,&m);
init(n);
for(int i=1;i<=m;i++)
{
int x;
scanf("%d",&x);
book[x]=true;
root=x;
}
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
}
mx=0;
dfs(root,0,0);
mx=0;
dfs(root,0,0);
printf("%d.00\n",mx/2);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
F.Permutation(hdu6235)
水題,隨便構造一下就好。。。
程式碼:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int a[MAXN];
void solve()
{
int n;
scanf("%d",&n);
int cnt=1;
for(int i=1;i<=n;i+=2)
{
a[i]=cnt++;
}
for(int i=2;i<=n;i+=2)
{
a[i]=cnt++;
}
printf("%d",a[1]);
for(int i=2;i<=n;i++)
{
printf(" %d",a[i]);
}
printf("\n");
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
H.A Simple Stone Game(hdu6237)
首先我們將陣列a的所有值加起來得到sum,然後求sum的所有的素因子,然後直接列舉所有的素因子就好了,設我們當前的素因子為p,我們對a陣列所有的值對p取模,然後按照從大到小排序,然後貪心的移動就好了(每次把最大的填到p),存素因子的陣列不用long long wa的莫名其妙?
程式碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e5+5;
int a[MAXN],b[MAXN];
ll pre[MAXN];
vector<ll> sv;
void Prime(ll x)
{
sv.clear();
for(ll i=2;i*i<=x;i++)
{
if(x%i==0) sv.push_back(i);
while(x%i==0) x/=i;
}
if(x!=1) sv.push_back(x);
}
void solve()
{
int n;
ll sum=0,ans=1e18;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
Prime(sum);
for(int i=0;i<sv.size();i++)
{
ll p=sv[i],tot=0;
for(int j=1;j<=n;j++)
b[j]=a[j]%p;
sort(b+1,b+1+n);
pre[0]=0;
for(int j=1;j<=n;j++)
pre[j]=pre[j-1]+b[j];
for(int j=n;j>=1;j--)
{
if(tot==pre[j]) break;
tot+=p-b[j];
}
ans=min(ans,tot);
}
printf("%lld\n",ans);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
J.Interview(hdu6239)
我們隊猜出了個線性的式子...可能正解要用到i^k (k<=3)的求和公式,不知道我們這個式子為啥是對的...可能最後化簡出來就是我們的式子吧,具體看程式碼
程式碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD=1e9+7;
ll qpow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1)
{
ret*=a;
ret%=MOD;
}
a=(a*a)%MOD;
b>>=1;
}
return ret;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
int n,d;
scanf("%d%d",&n,&d);
if(d==1)
printf("%lld\n",(2LL*n+4LL)%MOD*qpow(8,MOD-2)%MOD);
else
printf("%lld\n",(3LL*n+6LL)%MOD*qpow(8,MOD-2)%MOD);
}
return 0;
}
K.Server(hdu6240)
分數規劃經典問題哦,感覺自己分數規劃還是掌握的不太好啊,看了zzq神犇的部落格重新回憶了一下,發現自己為啥在求最大值啊?首先肯定是要二分答案的,然後,就是轉化為最小值是否大於0的問題,對於最小值的問題,可以直接進行dp,我們首先按照右端點進行排序,對於將所有花費變為a-mid*b,然後對於所有花費為負的線段,我們一定會選,所以我們首先把花費為負的線段花費變成0,然後進行dp,最後將所有的為負的花費的權值加入,即可得到最小花費,dp的過程要用線段樹來維護。好像跑最短路也可以?
程式碼:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
const double INF=1e9;
const double eps=1e-6;
int n,t;
struct monitor
{
int s,t,a,b;
double val;
bool operator < (const monitor &o)const
{
return t<o.t;
}
}sv[MAXN];
struct seg
{
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
double mi[MAXN<<2];
void push_up(int rt)
{
mi[rt]=min(mi[rt<<1],mi[rt<<1|1]);
}
void build(int l,int r,int rt)
{
mi[rt]=INF;
if(l==r)
return ;
int mid=(l+r)>>1;
build(lson);
build(rson);
}
void update(int pos,double val,int l,int r,int rt)
{
if(l==r)
{
mi[rt]=min(val,mi[rt]);
return ;
}
int mid=(l+r)>>1;
if(mid>=pos)
update(pos,val,lson);
if(mid<pos)
update(pos,val,rson);
push_up(rt);
}
double query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
return mi[rt];
}
int mid=(l+r)>>1;
double ret=INF;
if(L<=mid)
ret=min(ret,query(L,R,lson));
if(mid<R)
ret=min(ret,query(L,R,rson));
return ret;
}
}se;
double judge(double mid)
{
for(int i=1;i<=n;i++)
{
sv[i].val=sv[i].a-mid*sv[i].b;
}
se.build(0,t,1);
se.update(0,0,0,t,1);
double sum=0;
for(int i=1;i<=n;i++)
{
double val=se.query(sv[i].s-1,sv[i].t,0,t,1);
double add=sv[i].val;
if(add<0)
sum+=add,add=0;
se.update(sv[i].t,val+add,0,t,1);
}
double val=se.query(t,t,0,t,1);
return val+sum;
}
void solve()
{
scanf("%d%d",&n,&t);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&sv[i].s,&sv[i].t,&sv[i].a,&sv[i].b);
}
sort(sv+1,sv+1+n);
int cnt=25;
double ans=INF;
double l=0,r=1005;
while(cnt--)
{
double mid=(l+r)/2;
double res=judge(mid);
if(res>0)
{
l=mid;
ans=mid;
}
else
{
r=mid;
}
}
printf("%.3lf\n",ans);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
L.Color a Tree(hdu6241)
二分答案,然後將子樹外的問題轉換成子樹內最多有多少點的問題,然後從葉子開始合併區間,判斷每個節點是否可行,最後還要判斷二分的值是否在根的可行區間當中。
程式碼:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
struct edge
{
int v,nxt;
}E[MAXN*2];
struct node
{
int up,down;
}tr[MAXN];
struct lim
{
int id,num;
}A[MAXN],B[MAXN];
int head[MAXN],sz[MAXN],tot,n,na,nb;
bool flag;
void init()
{
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v)
{
E[tot].v=v;E[tot].nxt=head[u];head[u]=tot++;
E[tot].v=u;E[tot].nxt=head[v];head[v]=tot++;
}
void getlim(int mid)
{
for(int i=1;i<=n;i++)
{
tr[i].down=0,tr[i].up=sz[i];
}
for(int i=1;i<=na;i++)
{
int id=A[i].id,num=A[i].num;
tr[id].down=max(tr[id].down,num);
}
for(int i=1;i<=nb;i++)
{
int id=B[i].id,num=B[i].num;
tr[id].up=min(tr[id].up,mid-num);
}
for(int i=1;i<=n&&flag;i++)
{
if(tr[i].down>tr[i].up)
flag=false;
}
}
void dfssz(int now,int fa)
{
sz[now]=1;
for(int i=head[now];~i;i=E[i].nxt)
{
int v=E[i].v;
if(v==fa)
continue;
dfssz(v,now);
sz[now]+=sz[v];
}
}
void dfslim(int now,int fa)
{
int down=0,up=0;
for(int i=head[now];(~i)&&flag;i=E[i].nxt)
{
int v=E[i].v;
if(v==fa)
continue;
dfslim(v,now);
down+=tr[v].down,up+=tr[v].up;
}
tr[now].down=max(tr[now].down,down);
tr[now].up=min(tr[now].up,up+1);
//printf("%d:%d %d\n",now,tr[now].down,tr[now].up);
if(tr[now].down>tr[now].up)
flag=false;
}
void judge(int mid)
{
getlim(mid);
dfslim(1,0);
if(!(tr[1].down<=mid&&mid<=tr[1].up))
flag=false;
}
void solve()
{
init();
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
}
dfssz(1,0);
scanf("%d",&na);
for(int i=1;i<=na;i++)
{
scanf("%d%d",&A[i].id,&A[i].num);
}
scanf("%d",&nb);
for(int i=1;i<=nb;i++)
{
scanf("%d%d",&B[i].id,&B[i].num);
}
int l=0,r=n;
int ans=-1;
while(l<=r)
{
flag=true;
int mid=(l+r)>>1;
judge(mid);
if(flag)
{
r=mid-1;
ans=mid;
}
else
{
l=mid+1;
}
}
printf("%d\n",ans);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
M.Geometry Problem(hdu6242)
理性分析一下,就會發現,隨機三個點然後求圓心即可,期望是8次就能隨中。
程式碼:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
const double eps=1e-6;
int sgn(double x)
{
if(fabs(x)<eps)return 0;
if(x<0)return -1;
else return 1;
}
struct Point
{
double x,y;
Point(double _x=0,double _y=0):x(_x),y(_y){}
double operator ^ (const Point &b)const//叉積
{
return x*b.y-y*b.x;
}
Point operator - (const Point &b)const
{
return Point(x-b.x,y-b.y);
}
}P[MAXN];
struct Traingle
{
Point p[3];
Traingle(){}
Traingle(Point a,Point b,Point c)
{
p[0]=a;p[1]=b;p[2]=c;
}
};
struct Circle
{
Point center;
double r;
};
double Dis(Point a,Point b)
{
double dx=a.x-b.x;
double dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
}
double Area(Traingle T)
{
Point A=T.p[0]-T.p[1];
Point B=T.p[1]-T.p[2];
return (A^B)/2;
}
Circle CircumCircle(Traingle T)
{
Circle ret;
double a,b,c,c1,c2;
double xA,yA,xB,yB,xC,yC;
a=Dis(T.p[0],T.p[1]);
b=Dis(T.p[1],T.p[2]);
c=Dis(T.p[2],T.p[0]);
ret.r=(a*b*c)/(Area(T)*4.0);
xA=T.p[0].x;yA=T.p[0].y;
xB=T.p[1].x;yB=T.p[1].y;
xC=T.p[2].x;yC=T.p[2].y;
c1=(xA*xA+yA*yA-xB*xB-yB*yB)/2;
c2=(xA*xA+yA*yA-xC*xC-yC*yC)/2;
ret.center.x=(c1*(yA-yC)-c2*(yA-yB))/((xA-xB)*(yA-yC)-(xA-xC)*(yA-yB));
ret.center.y=(c1*(xA-xC)-c2*(xA-xB))/((yA-yB)*(xA-xC)-(yA-yC)*(xA-xB));
return ret;
}
void gettwo(Point a,Point b)
{
double x=(a.x+b.x)/2;
double y=(a.y+b.y)/2;
printf("%lf %lf %lf\n",x,y,Dis(a,Point(x,y)));
}
int id[3],n;
void getid()
{
id[0]=rand()%n+1;
id[1]=rand()%n+1;
id[2]=rand()%n+1;
while(id[1]==id[0])
id[1]=rand()%n+1;
while(id[2]==id[0]||id[2]==id[1])
id[2]=rand()%n+1;
}
int judge(Point a,Point b,Point c)
{
Point A=a-b;
Point B=c-b;
return sgn(A^B);
}
void solve()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&P[i].x,&P[i].y);
}
if(n==1)
{
printf("%lf %lf %d\n",P[1].x-1,P[1].y,1);
return ;
}
if(n>=2&&n<=4)
{
gettwo(P[1],P[2]);
return ;
}
while(1)
{
memset(id,0,sizeof(id));
getid();
if(!judge(P[id[0]],P[id[1]],P[id[2]]))
continue;
Circle res=CircumCircle(Traingle(P[id[0]],P[id[1]],P[id[2]]));
int cnt=0;
for(int i=1;i<=n;i++)
{
double dis=Dis(P[i],res.center);
if(sgn(dis-res.r)==0)
cnt++;
}
if(cnt>=(n+1)/2)
{
printf("%lf %lf %lf\n",res.center.x,res.center.y,res.r);
return ;
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int T;
srand(0);
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}