2018.8.28 练习赛
T1 机房排座
题解:
有解的充要性条件:
1.任意一个班级的同学个数不能多于机房空位置数的一半
2.我们把机房的格子进行黑白染色。按班级人数由多到少落座。先坐白色格子,再坐黑色格子。
样例2:
code:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
int n,m,k,maxx=0,i,j,t=1,r[102][102];
struct node
{
int id,num;
bool operator<(const node &u)const{return u.num<num;}
}a[10002];
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(i=1;i<=k;i++)
{
scanf("%d",&a[i].num);
maxx=max(maxx,a[i].num);
a[i].id=i;
}
sort(a+1,a+k+1);
if(maxx*2>n*m+1)
{
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)printf("0 ");
putchar('\n');
}
return 0;
}
if(m&1)
{
for(i=0;i<n*m;i+=2)
{
r[i/m+1][i%m+1]=a[t].id;
a[t].num--;
if(a[t].num==0)t++;
}
for(i=1;i<n*m;i+=2)
{
r[i/m+1][i%m+1]=a[t].id;
a[t].num--;
if(a[t].num==0)t++;
}
}
else
{
for(i=0;i<n*m;i+=2)
{
r[i/m+1][i%m+1+((i/m)&1)]=a[t].id;
a[t].num--;
if(a[t].num==0)t++;
}
for(i=1;i<n*m;i+=2)
{
r[i/m+1][i%m+1-((i/m)&1)]=a[t].id;
a[t].num--;
if(a[t].num==0)t++;
}
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)printf("%d ",r[i][j]);
putchar('\n');
}
return 0;
}
T2 大佬玩游戏
题解:
对于第\(i\)个人,设被整除的概率为\(p[i]\),则与他相邻的\(i+1\)个人与他之积能被整除的概率为\(p[x]=p[i]+p[j]-p[i]*p[j]\),因此,\(ans=\sum(2000*p[x])\)
code:
#include<stdio.h>
#include<algorithm>
#include<ctype.h>
#define val 2000
#define ld double
#define ll long long
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc() {
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
void read(T &x) {
char tt;
bool flag=0;
while(!isdigit(tt=gc())&&tt!='-');
tt=='-'?(x=0,flag=1):(x=tt-'0');
while(isdigit(tt=gc())) x=x*10+tt-'0';
if(flag) x=-x;
}
ll n,k;
ld ans;
ld p[100005];
int main() {
read(n),read(k);
for(int i=1; i<=n; i++) {
ll x,y;
read(x),read(y);
p[i]=1.0*(y/k-(x-1)/k)/(y-x+1);
}
for(int i=1; i<=n; i++)
ans+=(p[i]+p[(i+1)>n?1:i+1]-p[i]*p[(i+1)>n?1:i+1])*val;
printf("%lf",ans);
}
T3 方块消除
题解:
从左往右读入方块,读到某数字第一次出现的时候记录位置L,第二次出现的时候位置\(R\),统计在\((L,R)\)区间没有被消除掉的方块的个数K。那么消除\(L\),\(R\)这一对方块需要进行\(K\)次交换。然后将\(L\)和\(R\)消除掉。
我们需要记录\([L,R]\)区间中未消除的方块的个数,用树状数组。
当某数字\(x\)第一次出现时,设出现的位置为\(L\)。我们标记\(Pos[x]=L\)。并把它加入树状数组\(Modify(x,1)\)
当数字\(x\)第二次出现时,设出现位置为\(R\)。我们统计\((L,R)\)区间未被消除的数字个数\(K\)。用树状数组进行区间查询即可。\(K=getSum[R]-getSum(Pos[x]-1)\);\(ans+=k\);
然后消除掉\(L\)和\(R\),即\(Modify(Pos[x],-1)\)
code:
#include<stdio.h>
#include<algorithm>
#include<ctype.h>
#define ll long long
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc() {
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
void read(T &x) {
char tt;
bool flag=0;
while(!isdigit(tt=gc())&&tt!='-');
tt=='-'?(x=0,flag=1):(x=tt-'0');
while(isdigit(tt=gc())) x=x*10+tt-'0';
if(flag) x=-x;
}
ll n;
ll ans;
ll a[1000005];
ll c[1000005];
ll pos[1000005];
bool book[1000005];
ll lowbit(ll x) {
return x&-x;
}
void modify(ll x,ll d) {
for(ll i=x; i<=1000000; i+=lowbit(i))
c[i]+=d;
}
ll getsum(ll x) {
ll tmp=0;
for(ll i=x; i; i^=lowbit(i))
tmp+=c[i];
return tmp;
}
int main() {
read(n);
for(ll i=1; i<=(n<<1); i++) {
read(a[i]);
if(book[a[i]]) {
ans+=getsum(i)-getsum(pos[a[i]]);
modify(pos[a[i]],-1);
} else book[a[i]]=1,pos[a[i]]=i,modify(i,1);;
}
printf("%lld",ans);
}
T4 财务信息
题解:
考虑使用并查集来维护前缀和:
记\(s[i]=a[1]+a[2]+...+a[i]\),即\(s[i]\)为前缀和。
令\(v[i]=s[i]-s[fa[i]]\),其中\(fa[i]\)为\(i\)的父亲。
对于每个读入的\(x,y,z\),将\(x,y\)视为结点:
- 如果\(x,y\)的根结点相同,即\(fa[x]==fa[y]\),又因为\(v[y]-v[x]=s[y]-s[fa[y]]-(s[x]-s[fa[x]])=s[y]-s[x]\),所以\(v[y]-v[x]\)就是区间\([x,y]\)的和,所以只需要判断\(v[y]-v[x]\)是否等于\(z\)就可以了。
- 如果\(x\)与\(y\)的根结点不相同,合并两个节点并更新信息:
将y合并到\(x\)所在集合,\(x\)的根变为\(y\)的根的父亲\(v[fa[y]]=v[x]+z-v[y]\),\(a[fa[y]]=fa[x]\)
code:
#include<cstdio>
#include<iostream>
using namespace std;
int n,m,x,y,z,u,v,cas;
int f[105],dis[105];
int a[1005],b[1005],c[1005];
int get(int x)
{
if(f[x]==x) return x;
int t=f[x];
f[x]=get(f[x]);
dis[x]=dis[x]+dis[t];
return f[x];
}
int main()
{
cin>>cas;
while(cas--)
{
cin>>n>>m;
for(int i=0;i<=n;i++)
{
f[i]=i;
dis[i]=0;
}
int ok=0;
for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);
for(int i=1;i<=m;i++)
{
x=a[i],y=b[i],z=c[i];
x--;
u=get(x);
v=get(y);
if(u==v)
{
if(dis[y]-dis[x]!=z)
{
ok=1;
cout<<"false"<<endl;
break;
}
}
else
{
f[v]=u;
dis[v]=dis[x]-dis[y]+z;
}
}
if(ok==0) cout<<"true"<<endl;
}
return 0;
}