题意: 相当于均分纸牌,使这几个数的权值相等
分析: 先求出平均数,然后如果大于平均数,直接加上多余的部分,这就是答案
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,tot;
int a[N];
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]),tot+=a[i];
tot/=n;
int ans=0;
for (int i=1;i<=n;i++)
if(a[i]>tot) ans+=a[i]-tot;
printf("%d\n",ans);
return 0;
}
B Umbrellas for Cows
题意: 将一条数轴上的点都覆盖的最小代价
分析: 假设 f [ i ] 表示将前i个覆盖的最小代价,如果要最小代价,这个伞的左端点肯定是坐标 i 之前的某
一个 j 的坐标,然后他们的距离是 a [ i ] - a [ j ],所需的伞的最小长度是 a [ i ] - a [ j ] + 1,然而伞的长度和价格没
有关系,也就是说,我们要在a [ i ] - a [ j ] + 1 ~ m 的范围里找一个价格最小的,只需要用一个数组记录一下
代码:
/*
思考:
当前为i,设f[i]表示覆盖前i个点的最小代价
f[i]=min(f[j]+min(c[m-j+1~m])
*/
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N],c[N],st[N],f[N];
int main()
{
memset(f,0x3f,sizeof(f));
scanf("%d%d",&n,&m);f[0]=0;
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
for (int i=1;i<=m;i++) scanf("%d",&c[i]);
//价格
st[m+1]=0x3f3f3f3f;c[0]=0x3f3f3f3f;
for (int i=m;i>=0;i--) st[i]=min(st[i+1],c[i]);
for (int i=1;i<=n;i++)
for (int j=i;j>=1;j--)
f[i]=min(f[i],f[j-1]+st[a[i]-a[j]+1]);
printf("%d\n",f[n]);
return 0;
}
C Roadblock
题意: 是某一条边的边权加倍,使原1->n的最短路与现在的最短路差值最大
分析: n范围较小。同时如果要改边,肯定要改最短路上的。不然你改其他路上的,压根就不会走。所以在第一次求
最短路的时候,记录一下转移方向。然后枚举最短路上的边,每改一条就求出当前的最短路,用一个变量记录最大
差值就行啦
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=110,M=11100;
int n,m,tot,ans,cnt;
int f[N],pre[N],las[N],ch[M];
int h[N],nex[M<<1],ver[M<<1],pri[M<<1];
bool vis[N];
inline void add(int x,int y,int z)
{
nex[tot]=h[x];
ver[tot]=y;
pri[tot]=z;
h[x]=tot++;
}
inline void spfa()
{
memset(f,0x3f,sizeof(f));
f[1]=0;
queue<int>q;
q.push(1);vis[1]=1;
while(!q.empty())
{
int k=q.front();q.pop();
for (int i=h[k];~i;i=nex[i])
{
int j=ver[i];
if(f[j]>f[k]+pri[i])
{
pre[j]=k,las[j]=i;
f[j]=f[k]+pri[i];
if(!vis[j])
vis[j]=1,q.push(j);
}
}vis[k]=0;
}
}
int main()
{
memset(h,-1,sizeof(h));
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
spfa();
int now=n;
while(now!=1)
{
ch[++cnt]=las[now];
now=pre[now];
}
int li=f[n],ans=0;
for (int i=1;i<=n;i++)
{
pri[ch[i]]*=2;
pri[ch[i]^1]*=2;
spfa();
ans=max(ans,f[n]-li);
pri[ch[i]]/=2;
pri[ch[i]^1]/=2;
}
printf("%d\n",ans);
return 0;
}
D Cow Photography(Silver)
分析: 一个结论题。假设有A,B两头奶牛,因为每头奶牛最多只移动一次,且最多只会改变两次先后顺序。即第一
次,奶牛B会换到A前面,第二次A可能又会换到B前面。那么通俗来说,只要有三张照片里,A的位置在B之前,那
么原来A就在B前面
代码:
#pragma GCC optimize(3,"inline","Ofast","fast-math","no-stack-protector","unroll-loops")
#pragma GCC target("sse","sse2","sse3","sse4","avx","avx2","popcnt")
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+10;
int n;
int a[N];
map<int,int> k[6];
inline bool god(int x,int y)
{
int num=0;
for (int i=1;i<=5;i++)
if(k[i][x]<k[i][y]) num++;
if(num>=3) return 1;
return 0;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=5;i++)
for (int j=1;j<=n;j++)
{
int x;scanf("%d",&x);
a[j]=x;
k[i][x]=j;
}
sort(a+1,a+n+1,god);
for (int i=1;i<=n;i++) printf("%d\n",a[i]);
return 0;
}
E Grass Planting
题意:使某一条路径上的所有路的权值加一,然后询问某一条边的权值
分析:感jio树链剖分是可以做。但是有另一种方法。通过题意,发现这很像差分。那么我们用dfs序记录每个节点
被遍历到的顺序,每次给边+1的时候,因为能管到的区域,是从lca到x点,lca到y点之间的路径,这个时候给将这
两个之间的路径-1,因为lca部分减了两次,加上2
代码:
//#pragma GCC optimize(3,"inline","Ofast","fast-math","no-stack-protector","unroll-loops")
//#pragma GCC target("sse","sse2","sse3","sse4","avx","avx2","popcnt")
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define RG register
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e5+10;
int n,m,cnt,tot;
int f[N][21],dep[N],d[N];
int a[N],L[N],R[N],head[N];
struct nkl
{
int nex,to;
}e[N<<1];
inline void add(int x,int y)
{
e[++cnt].nex=head[x];
e[cnt].to=y;
head[x]=cnt;
}
inline void dfs(int u,int pre)
{
dep[u]=dep[pre]+1;
f[u][0]=pre;
L[u]=++tot;
for (int i=1;i<=18;i++)
f[u][i]=f[f[u][i-1]][i-1];
for (int i=head[u];i;i=e[i].nex)
{
int v=e[i].to;
if(v==pre) continue;
dfs(v,u);
}
R[u]=tot;
}
inline int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for (int i=18;i>=0;i--)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y) return x;
for (int i=18;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
inline int lowbit(int x)
{
return x&(-x);
}
inline void ad(int i,int k)
{
while(i<=n)
{
d[i]+=k;
i+=lowbit(i);
}
}
inline int get(int i)
{
int ans=0;
while(i>0)
{
ans+=d[i];
i-=lowbit(i);
}
return ans;
}
int main()
{
cin>>n>>m;
for (int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
add(x,y),add(y,x);
}
dfs(1,0);
while(m--)
{
char ins;int x,y;
cin>>ins;
cin>>x>>y;
if(ins=='P')
{
int lca=LCA(x,y);
ad(L[x],1);
ad(L[y],1);
ad(L[lca],-2);
}
else
{
if(dep[x]<dep[y]) x=y;
printf("%d\n",get(R[x])-get(L[x]-1));
}
}
return 0;
}
F Simplifying the Farm
似乎是最有价值的一道题。
/*
对于这样一棵重边很少的生成树
重边要么取,要么不取,先找重边,然后
取出可以加入的边的数量,然后通过乘
法原理求解
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=4e4+10,M=1e5+10;
const ll mod=1e9+7;
int n,m;
int f[N];
struct nkl
{
int u,v;ll p;
bool operator < (const nkl &b)const
{
return p<b.p;
}
}s[M];
inline int find(int x)
{
if(x==f[x]) return x;
return f[x]=find(f[x]);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d%lld",&s[i].u,&s[i].v,&s[i].p);
sort(s+1,s+m+1);
for (int i=1;i<=n;i++) f[i]=i;
ll ans=0,num=1;
for (int i=1;i<=m;)
{
int j;
set<pair<int,int> >t;
ll cnt=0,sum=0;
for (j=i;j<=m;j++)
{
int u=find(s[j].u),v=find(s[j].v);
if(u>v) swap(u,v);
if(u!=v)
{
cnt++;
t.insert(make_pair(u,v));
}
if(s[j].p!=s[j+1].p) break;
}//相同权值的边数量
for (;i<=j;i++)
{
int u=find(s[i].u),v=find(s[i].v);
if(u!=v)
{
f[v]=u;
sum++;
ans+=s[i].p;
}
}//已加入的边的数量
if(sum==1) num=num*cnt%mod;
//只有一条,随便加
if(sum==2)
{
if(cnt==3&&t.size()==2) num=num*2%mod;
//有等价边,
if(cnt==3&&t.size()==3) num=num*3%mod;
//无等价边,
}
}
printf("%lld %lld\n",ans,num);
return 0;
}
G Cow Photography
和D题一样
H Cow Beauty Pageant (Bronze)
分析:直接求出两个连通块中的点,如果要连接,那肯定是其中最近的两个点的距离减-1
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=55;
int n,m,tot,ans=0x3f3f3f3f;
int a[N][N],cnt[N];
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
bool vis[N][N];
struct nkl
{
int dx,dy;
};
nkl k[5][N*N/3];
inline bool check(int x,int y)
{
return x<1||x>n||y<1||y>m;
}
inline void bfs(int x,int y)
{
tot++;
queue<int>u,v;
u.push(x),v.push(y);
vis[x][y]=1;
while(u.size())
{
int x=u.front(),y=v.front();
u.pop(),v.pop();
k[tot][++cnt[tot]].dx=x,k[tot][cnt[tot]].dy=y;
for (int i=0;i<4;i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(check(xx,yy)) continue;
if(vis[xx][yy]) continue;
if(!a[xx][yy]) continue;
vis[xx][yy]=1;
u.push(xx),v.push(yy);
}
}
}
int main()
{
cin>>n>>m;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
char c;cin>>c;
if(c=='X') a[i][j]=1;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if(!a[i][j]) continue;
if(vis[i][j]) continue;
bfs(i,j);
}
for (int i=1;i<=cnt[1];i++)
for (int j=1;j<=cnt[2];j++)
ans=min(ans,abs(k[1][i].dx-k[2][j].dx)+abs(k[1][i].dy-k[2][j].dy));
printf("%d\n",ans-1);
return 0;
}
I Moo Sick
题意:求出一个序列A中有多少子段和序列B相似。这里的相似定义存在某一个数,使B数组同时加上他时,所有的
数是A的某一个子段
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+10;
int n,c,tot;
int a[N],k[N],b[N],ans[N];
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&c);
for (int i=1;i<=c;i++) scanf("%d",&k[i]);
sort(k+1,k+c+1);
for (int i=1;i<=n;i++)
{
int cnt=0;
if(i+c-1>n) break;
for (int p=i+c-1;p>=i;p--) b[++cnt]=a[p];
sort(b+1,b+cnt+1);
bool ok=0;
for (int p=2;p<=cnt;p++)
if(k[p]-b[p]!=k[p-1]-b[p-1]) ok=1;
if(!ok) ans[++tot]=i;
}
printf("%d\n",tot);
for (int i=1;i<=tot;i++) printf("%d\n",ans[i]);
return 0;
}
J Awkward Digits
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100;
int cnt;
char a[N],b[N],c[N];
inline bool check(ll ok,int bb)
{
cnt=0;
while(ok)
{
c[cnt++]=(ok%3+'0');
ok/=3;
}
int num=0;
if(cnt!=bb) return 0;
for (int i=0;i<bb;i++)
if(c[i]!=b[bb-i-1]) num++;
if(num==1) return 1;
return 0;
}
int main()
{
scanf("%s%s",a,b);
int lena=strlen(a),lenb=strlen(b);
ll now=0;
for (int i=0;i<lena;i++) now=now*2+(a[i]-'0');
for (int i=1;i<lena;i++)
{
int c=a[i]-'0';
ll th=now-c*(1<<(lena-i-1))+(c^1)*(1<<(lena-i-1));
if(check(th,lenb))
{
printf("%lld\n",th);
return 0;
}
}
return 0;
}
K Contest Timing
分析:数据不大,直接求出所有的分钟数,相减就行
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a,b,c;
int main()
{
scanf("%lld%lld%lld",&a,&b,&c);
ll las=1440*11+11*60+11;
ll now=1440*a+b*60+c;
if(now-las<0) puts("-1");
else printf("%lld\n",now-las);
return 0;
}
Tile Exchanging
分析:本来以为我的暴力程序不会过的,结果....我直接记录f [ i ] [ j ] 表示前i个矩形使面积为 j 的最小
代价,然后三重循环,第一层当前编号,第二层构成面积,第三层,边长
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=14,M=1e4+10;
int n,m;
int a[N],f[N][M];
int main()
{
memset(f,0x3f,sizeof(f));f[0][0]=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)//枚举面积
for (int k=1;k<=100;k++)//枚举边长
{
if(k*k>j) break;
f[i][j]=min(f[i][j],f[i-1][j-k*k]+(a[i]-k)*(a[i]-k));
}
}
if(f[n][m]>1e9) puts("-1");
else printf("%d\n",f[n][m]);
return 0;
}