題目編號是我瞎搞的
T1
因爲數據範圍很小
我還以爲是折半搜索
沒想到是個n3的DP
不難看出
跳樓的高度是單調的情況下才可能是最優
這樣就先把高度排序
轉移方程
dp[i][j] = min ( dp[i-1][k] + b[j].h - b[k].h ) + b[j].c ( k∈1~j-1 )
i表示跳了幾次樓
j表示現在在哪座樓上
dp[i][j]表示跳i次樓到達j樓需要的最小花費
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<climits>
#include<queue>
#include<ctime>
#define LL long long
using namespace std;
struct nico
{
int c,h;
}b[55];
int dp[55][55],n,t;
bool comp(nico a,nico b)
{
return a.h<b.h;
}
int main()
{
freopen("meet.in","r",stdin);
freopen("meet.out","w",stdout);
int i,j,k;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&b[i].c);
for(i=1;i<=n;i++) scanf("%d",&b[i].h);
scanf("%d",&t);
sort(b+1,b+n+1,comp);
memset(dp,127,sizeof(dp));
for(i=1;i<=n;i++) dp[0][i]=b[i].c;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
for(k=1;k<j;k++)
dp[i][j]=min(dp[i][j],dp[i-1][k]+b[j].h-b[k].h);
dp[i][j]+=b[j].c;
}
for(i=n;i>=0;i--)
for(j=1;j<=n;j++)
if(dp[i][j]<=t)
{
printf("%d",i+1);
return 0;
}
printf("0");
return 0;
}
T2
這題什麼算法什麼數據結構都沒考
設原數列爲a
兩兩相加後形成的數列爲b
當場倒是想出來了
把a b升序排序
b1一定是a1+a2
b2一定是a1+a3
然後就不知道怎麼辦了
打了個暴力了事
實際上
再往後想的話
就會發現
我們並不知道a2+a3是多少
但如果a2+a3確定下來
a1 a2 a3就都能確定了
a1 a2 a3都確定下來以後
b數列中除去a1+a2 a1+a3 a2+a3
剩下的最小的一定是a1+a4
重複此過程
關於字典序
a2+a3從小到大枚舉
這就意味着構造出的a1是從大到小的
又因爲a1不可能重複
所以答案字典序可以確定爲降序
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<climits>
#include<queue>
#define LL long long
using namespace std;
int n,m,cnt,s[45005],r[305],ans[305][305];
bool use[45005];
void check(int p)
{
int i,j,k,tmp,px,pos;
if((s[1]+s[2]+s[p])%2) return;
memset(use,0,sizeof(use));
r[1]=(s[1]+s[2]+s[p])/2-s[p];
r[2]=s[1]-r[1];
r[3]=s[2]-r[1];
use[1]=1;use[2]=1;use[p]=1;
for(i=4,j=3;i<=n;i++)
{
while(j<=m&&use[j]) j++;
if(j>m) return;
r[i]=s[j]-r[1];
use[j]=1;
for(k=2;k<i;k++)
{
if(r[k]>r[i]) return;
tmp=r[k]+r[i];
pos=lower_bound(s+1,s+m+1,tmp)-s;
if(s[pos]!=tmp) return;
px=pos;
while((px<=m)&&(use[px])&&(s[px]==s[pos])) px++;
if((px>m)||(use[px])||(s[px]!=s[pos])) return;
use[px]=1;
}
}
cnt++;
for(i=1;i<=n;i++)
ans[cnt][i]=r[i];
}
int main()
{
freopen("city.in","r",stdin);
freopen("city.out","w",stdout);
int i,j;
scanf("%d",&n);
m=n*(n-1)/2;
for(i=1;i<=m;i++) scanf("%d",&s[i]);
sort(s+1,s+m+1);
for(i=3;i<=m;)
{
check(i);
j=i;
while(j<=m&&s[j]==s[i]) j++;
i=j;
}
printf("%d\n",cnt);
for(i=1;i<=cnt;i++)
{
for(j=1;j<=n;j++)
printf("%d ",ans[i][j]);
printf("\n");
}
return 0;
}
T3
看起來完全不會做
看了別人的寫法
線段樹還有這種操作
因爲k<=10
所以只需要記錄前十大
加了個重載運算符
合併的時候會簡便很多
因爲線段樹常數很大
還以爲會T
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<climits>
#include<queue>
#include<ctime>
#define LL long long
#define MOD 1000000007
using namespace std;
int n,m,num[100005],lazy[400005];
struct nico
{
int res[11];
nico operator + (const nico &b)const
{
nico z;
int p1=0,p2=0,i;
for(i=0;i<10;i++)
{
if(res[p1]>b.res[p2]) z.res[i]=res[p1++];
else z.res[i]=b.res[p2++];
}
return z;
}
}tree[400005];
void build(int l,int r,int p)
{
if(l==r)
{
tree[p].res[0]=num[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,p<<1);
build(mid+1,r,(p<<1)|1);
tree[p]=tree[p<<1]+tree[(p<<1)|1];
}
void color(int p,int c)
{
int i;
lazy[p]+=c;
for(i=0;i<10;i++)
if(tree[p].res[i]) tree[p].res[i]+=c;
}
void pushdown(int p)
{
if(lazy[p])
{
color(p<<1,lazy[p]);
color((p<<1)|1,lazy[p]);
lazy[p]=0;
}
}
nico ask(int l,int r,int L,int R,int p)
{
if(L<=l&&R>=r) return tree[p];
int mid=(l+r)>>1;
pushdown(p);
if(R<=mid) return ask(l,mid,L,R,p<<1);
if(L>mid) return ask(mid+1,r,L,R,(p<<1)|1);
if(L<=mid&&R>mid) return ask(l,mid,L,mid,p<<1)+ask(mid+1,r,mid+1,R,(p<<1)|1);
}
void add(int l,int r,int L,int R,int p,int c)
{
if(L<=l&&R>=r)
{
color(p,c);
return;
}
int mid=(l+r)>>1;
pushdown(p);
if(R<=mid) add(l,mid,L,R,p<<1,c);
if(L>mid) add(mid+1,r,L,R,(p<<1)|1,c);
if(L<=mid&&R>mid)
{
add(l,mid,L,mid,p<<1,c);
add(mid+1,r,mid+1,R,(p<<1)|1,c);
}
tree[p]=tree[p<<1]+tree[(p<<1)|1];
}
int main()
{
freopen("noname.in","r",stdin);
freopen("noname.out","w",stdout);
int i,opt,l,r,p;
nico ans;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) scanf("%d",&num[i]);
build(1,n,1);
for(i=1;i<=m;i++)
{
scanf("%d%d%d%d",&opt,&l,&r,&p);
if(opt==0)
{
if(r-l+1<p) printf("-1\n");
else
{
ans=ask(1,n,l,r,1);
printf("%d\n",ans.res[p-1]);
}
}
if(opt==1) add(1,n,l,r,1,p);
}
return 0;
}
T4
打了個60分暴力
其實出題人可能想給80分
但是那20分沒想出來怎麼寫
40%直接枚舉就行
還有20%
因爲保證所有數都小於1023
所以開兩個桶
一個桶記原數
另一個記結果
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<climits>
#include<queue>
#include<ctime>
#define LL long long
#define MOD 1000000007
using namespace std;
int n,k,i,num[2005],p[10005],tot,ans,tmp,j,t,sum[2005];
bool comp(int a,int b)
{
return a>b;
}
int main()
{
freopen("war.in","r",stdin);
freopen("war.out","w",stdout);
scanf("%d%d",&n,&k);
if(n<=100)
{
for(i=1;i<=n;i++)
scanf("%d",&num[i]);
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
{
tot++;
p[tot]=num[i]^num[j];
}
sort(p+1,p+tot+1,comp);
for(i=1;i<=k;i++) ans=(ans+p[i])%MOD;
}
else
{
for(i=1;i<=n;i++)
{
scanf("%d",&tmp);
num[tmp]++;
}
for(i=0;i<=1023;i++)
if(num[i])
for(j=i;j<=1023;j++)
if(num[j])
{
if(j==i) t=num[j]-1;
else t=num[j];
tmp=i^j;
sum[tmp]+=num[i]*t;
}
for(i=1024;i>=0&&k>0;i--)
{
k-=sum[i];
if(k<0) ans=(ans+(k+sum[i])*i)%MOD;
else ans=(ans+sum[i]*i)%MOD;
}
}
printf("%d",ans);
return 0;
}
T5
這個題
太 抽 象 了
Kruscal求最大生成樹
在並查集合並時
把原本的一個根連向另一個根改成兩個根都連向一個新建的節點
並把當前正在處理的邊的權值賦給這個新節點做點權
這樣形成的結構會是一棵樹
點u的答案
大致上是樹的根到自己的路徑上
相鄰兩個節點的子樹葉節點數的平方和
需要注意
父子兩個節點權值相同的情況
這個部分需要特殊處理
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<climits>
#include<queue>
#include<ctime>
#define LL long long
#define MOD 1000000007
using namespace std;
struct nico
{
int u,v,l;
}e[500005];
int n,m,f[500005],val[500005],size[500005],son[500005][2];
LL ans[500005];
int read()
{
char ch=getchar();
int x=0;
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {x=x*10+(ch-'0');ch=getchar();}
return x;
}
bool comp(nico a,nico b)
{
return a.l>b.l;
}
int find(int x)
{
if(f[x]==x) return x;
f[x]=find(f[x]);
return f[x];
}
void dfs(int p,int fath,LL cnt)
{
LL tmp=0;
if(fath)
{
if(val[p]==val[fath]) size[p]=size[fath];
else tmp=1ll*(size[fath]-size[p])*(size[fath]-size[p]);
}
if(son[p][0])
{
dfs(son[p][0],p,cnt+tmp);
dfs(son[p][1],p,cnt+tmp);
}
else ans[p]=cnt+tmp;
}
int main()
{
freopen("car.in","r",stdin);
freopen("car.out","w",stdout);
int i,newp,uf,vf;
n=read();
m=read();
for(i=1;i<=m;i++)
{
e[i].u=read();
e[i].v=read();
e[i].l=read();
}
sort(e+1,e+m+1,comp);
for(i=1;i<=n;i++) f[i]=i;
for(i=1;i<=n;i++) size[i]=1;
newp=n;
for(i=1;i<=m;i++)
{
uf=find(e[i].u);
vf=find(e[i].v);
if(uf!=vf)
{
newp++;
f[uf]=newp;f[vf]=newp;
f[newp]=newp;
size[newp]=size[uf]+size[vf];
son[newp][0]=uf;son[newp][1]=vf;
val[newp]=e[i].l;
}
}
dfs(newp,0,0);
for(i=1;i<=n;i++) printf("%lld ",ans[i]);
return 0;
}
T6
離散化
然後開桶存起來
注意正反面相同的要特殊處理
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<climits>
#include<queue>
#include<vector>
using namespace std;
struct nico1
{
int a,b;
}t[300005];
struct nico2
{
int num,pos;
}up[600005];
int all[600005],tp,cnt,tot[600005],ans,i,n;
bool comp(nico2 a,nico2 b)
{
return a.num>b.num;
}
int main()
{
freopen("card.in","r",stdin);
freopen("card.out","w",stdout);
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d%d",&t[i].a,&t[i].b);
cnt++;
all[cnt]=t[i].a;
cnt++;
all[cnt]=t[i].b;
}
sort(all+1,all+cnt+1);
tp=unique(all+1,all+cnt+1)-all-1;
for(i=1;i<=n;i++)
{
t[i].a=lower_bound(all+1,all+tp+1,t[i].a)-all;
t[i].b=lower_bound(all+1,all+tp+1,t[i].b)-all;
}
for(i=1;i<=n;i++)
{
tot[t[i].a]++;tot[t[i].b]++;
if(t[i].a==t[i].b) tot[t[i].a]--;
up[t[i].a].num++;up[t[i].a].pos=t[i].a;
}
sort(up+1,up+tp+1,comp);
for(i=1;i<=tp;i++)
if(tot[up[i].pos]>=(n+1)/2)
{
ans=(n+1)/2-up[i].num;
if(ans<0) ans=0;
printf("%d",ans);
return 0;
}
printf("Impossible");
return 0;
}
T7
利用歸併排序的思想
把數換成字符串
其實我在瞎扯
#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<climits>
#include<queue>
#include<vector>
#define MOD 1000000007
#define LL long long
using namespace std;
LL pow[50005],hash[50005];
int n,m,ans,num[50005],f[50005],tmp[50005];
char s[50005];
bool compare(int p1,int p2)
{
int l,r,mid;
LL hash1,hash2;
if(p1==p2) return 1;
l=0;r=m+1;
if(n-p2+2<r) r=n-p2+2;
if(n-p1+2<r) r=n-p1+2;
while(r-l>1)
{
mid=(l+r)>>1;
hash1=(hash[p1+mid-1]-hash[p1-1]*pow[mid])%MOD;
hash2=(hash[p2+mid-1]-hash[p2-1]*pow[mid])%MOD;
hash1=(hash1+MOD)%MOD;hash2=(hash2+MOD)%MOD;
if(hash1==hash2) l=mid;
else r=mid;
}
if(l==m) return 1;
return num[p1+l]<num[p2+l];
}
void sort(int l,int r)
{
int l1,l2,mid,cnt,i;
if(l==r) return;
mid=(l+r)>>1;
sort(l,mid);sort(mid+1,r);
l1=l;
l2=mid+1;
cnt=l;
while(l1<=mid&&l2<=r)
{
if(compare(f[l1],f[l2]))
{
tmp[cnt]=f[l1];
cnt++;l1++;
}
else
{
tmp[cnt]=f[l2];
cnt++;l2++;
ans+=mid-l1+1;
}
}
while(l1<=mid)
{
tmp[cnt]=f[l1];
cnt++;l1++;
}
while(l2<=r)
{
tmp[cnt]=f[l2];
cnt++;l2++;
}
for(i=l;i<=r;i++) f[i]=tmp[i];
return;
}
int main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
int i;
scanf("%d%d",&n,&m);
scanf("%s",s);
pow[0]=1;
for(i=1;i<=n;i++)
{
num[i]=s[i-1]-'a'+1;
hash[i]=(hash[i-1]*29+num[i])%MOD;
pow[i]=(pow[i-1]*29)%MOD;
f[i]=i;
}
sort(1,n);
printf("%d",ans);
return 0;
}
T8
不難看出是個挺漂亮的二叉樹
因爲樹很淺
暴力求LCA
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<climits>
#include<queue>
#include<vector>
#define MOD 1000000007
#define LL long long
using namespace std;
int n,pow2[30],x,y,d,dep1,dep2,dep3,rp1,rp2;
int findep(int p)
{
int i;
return log2(p);
}
int lca(int p1,int p2)
{
dep1=findep(p1);
dep2=findep(p2);
rp1=dep1;rp2=dep2;
if(dep1<dep2)
{
swap(p1,p2);
swap(dep1,dep2);
}
while(dep2<dep1)
{
p1/=2;
dep1--;
}
while(p1!=p2)
{
p1/=2;
p2/=2;
}
dep3=findep(p1);
return rp1+rp2-2*dep3;
}
int main()
{
freopen("city.in","r",stdin);
freopen("city.out","w",stdout);
int i;
scanf("%d",&n);
pow2[0]=1;
for(i=1;i<=27;i++)
pow2[i]=pow2[i-1]*2;
for(i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
d=lca(x,y);
printf("%d\n",d);
}
return 0;
}
T9
想當年巧克力和香子蘭
還不是小黃油女主
只是兩個漂亮的妹子
甚至還是不少妹子的夢想
看着n很小
所以我們來狀壓吧
f[i][sta]表示從家到i經過sta這個狀態的最短距離
g[j][sta]表示從花店到j經過sta這個狀態的最短距離
拼起來
#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<climits>
#include<queue>
#include<vector>
#define MOD 1000000007
#define LL long long
using namespace std;
int i,j,k,sta,situ,n,m,dis[22][22],tot,f[20][1<<20],g[20][1<<20],x,y,z,num,minx,pos,ans,ans1,ans2;
int main()
{
freopen("vanilla.in","r",stdin);
freopen("vanilla.out","w",stdout);
scanf("%d%d",&n,&m);
memset(dis,127/3,sizeof(dis));
for(i=0;i<=n;i++) dis[i][i]=0;
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
dis[x][y]=min(dis[x][y],z);
dis[y][x]=min(dis[y][x],z);
}
if(n==3)
{
ans=(dis[0][1]+dis[1][2])*2;
printf("%d",ans);
return 0;
}
tot=(1<<(n-2))-1;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
for(k=0;k<n;k++)
dis[j][k]=min(dis[j][k],dis[j][i]+dis[i][k]);
memset(f,127/3,sizeof(f));
for(i=1;i<n-1;i++) f[i][1<<(i-1)]=dis[i][0];
for(sta=0;sta<=tot;sta++)
for(i=1;i<n-1;i++)
if((1<<(i-1))&sta)
for(j=1;j<n-1;j++)
if(!((1<<(j-1))&sta))
f[j][sta|(1<<(j-1))]=min(f[j][sta|(1<<(j-1))],f[i][sta]+dis[i][j]);
memset(g,127/3,sizeof(g));
for(i=1;i<n-1;i++) g[i][1<<(i-1)]=dis[i][n-1];
for(sta=0;sta<=tot;sta++)
for(i=1;i<n-1;i++)
if((1<<(i-1))&sta)
for(j=1;j<n-1;j++)
if(!((1<<(j-1))&sta))
g[j][sta|(1<<(j-1))]=min(g[j][sta|(1<<(j-1))],g[i][sta]+dis[i][j]);
ans=500000;
for(sta=0;sta<=tot;sta++)
{
situ=sta^tot;
num=0;
for(i=1;i<n-1;i++)
if((1<<(i-1))&sta) num++;
if(num!=(n-2)/2) continue;
ans1=500000;ans2=500000;
for(i=1;i<n-1;i++)
if((1<<(i-1))&sta)
for(j=1;j<n-1;j++)
if((1<<(j-1))&situ)
ans1=min(ans1,f[i][sta]+dis[i][j]+g[j][situ]);
for(i=1;i<n-1;i++)
if((1<<(i-1))&sta)
for(j=1;j<n-1;j++)
if((1<<(j-1))&situ)
ans2=min(ans2,g[i][sta]+dis[i][j]+f[j][situ]);
ans=min(ans,ans1+ans2);
}
printf("%d",ans);
return 0;
}