COMPFEST 14 - Preliminary Online Mirror(持续更新)
作者:互联网
Preface
9/10:今天不知道为什么一整天头疼的一批,而且牙也疼的吃不了饭,实在写不动题目啊
9/11:晚上发烧了,结果睡了一晚竟然好了……我的自愈能力原来这么强的嘛awa
9/12:得知错过了校队的第一轮选拔(没收到通知qaq),得等大一下才有机会了
不过自己写写题也比较轻松没什么压力,但接下来得准点打比赛了不能天天VP
下面题目做的顺序排,大致是按难度递增的把
A. Accumulation of Dominoes
SB题,注意特判\(m=1\)的情形
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int n,m;
int main()
{
scanf("%d%d",&n,&m); if (m==1) return printf("%d",n-1),0;
return printf("%lld",1LL*n*(m-1)),0;
}
B. Basketball Together
SB题,贪心地从大到小考虑每个人为队长,拉队员的话就从小的开始拉
#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,d,a[N],ans;
int main()
{
RI i,j; for (scanf("%d%d",&n,&d),i=1;i<=n;++i) scanf("%d",&a[i]);
for (++d,sort(a+1,a+n+1),i=0,j=n;i<j;--j)
{
int k=(d-1)/a[j]; if (i+k<j) i+=k,++ans; else break;
}
return printf("%d",ans),0;
}
G. Garage
貌似之前有过哪个题目有这个结论的说
直接说结论,\(x\)是合法的当且仅当\(x\)是大于等于\(3\)的奇数或大于等于\(8\)的\(4\)的倍数
证明,若\(x=2k+1,k\ge 1\),显然当\(b=k+1,a=k\)时符合,若\(x=4k+4,k\ge 1\),显然当\(b=k+2,a=k\)时符合
那么剩下来不符合的就是形如\(4k+2,k\ge 1\)的数了,不难发现由于\(a^2.b^2\)对\(4\)取模后的结果要么是\(0\)要么是\(1\),因此\(b^2-a^2\)对\(4\)取模的结果必不可能是\(2\)
计算答案的话可以直接二分,求\(\le x\)中有多少个合法的数即可
#include<cstdio>
#include<iostream>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
int n,ans;
inline int count(CI x)
{
return (x+1)/2-1+(x>=4?x/4-1:0);
}
signed main()
{
scanf("%lld",&n); int l=3,r=2147483647,mid;
while (l<=r)
{
mid=l+r>>1; if (count(mid)>=n) ans=mid,r=mid-1; else l=mid+1;
}
return printf("%lld",ans),0;
}
M. Moving Both Hands
一个朴素的想法,设\(d(x,y)\)表示\(x\)到\(y\)的最短路,考虑枚举中间点,对于\((1,v)\)的答案就是\(\min_\limits{1\le i\le n} d(1,i)+d(v,i)\)
将原图中的边反向,设\(d'(x,y)\)表示反向图中\(x\)到\(y\)的最短路,此时答案为\(\min_\limits{1\le i\le n} d(1,i)+d'(i,v)\)
考虑对每个点增加一个状态,设\((x,0/1)\)表示到\(x\)的最短路,第二维表示是否反向,此时有:
- 对于每条边\((u,v,w)\),连\((u,0)\to (v,0)\)权值为\(w\)的边,以及\((v,1)\to (u,1)\)权值为\(w\)的边
- 对于每个点\(v\),连\((v,0)\to (v,1)\),权值为\(0\)的边
这样直接跑一遍Dijkstra即可,最后点\(v\)的答案就是\((1,0)\)到\((v,1)\)的最短路
#include<cstdio>
#include<queue>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
const long long INF=1e18;
struct edge
{
int to,nxt,v;
}e[N<<2]; int n,m,head[N],cnt,x,y,z; long long dis[N]; bool vis[N];
struct data
{
long long v; int p;
inline data(const long long& V=0,CI P=0) { v=V; p=P; }
friend inline bool operator < (const data& A,const data& B)
{
return A.v>B.v;
}
}; priority_queue <data> hp;
inline void addedge(CI x,CI y,CI z)
{
e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
}
#define to e[i].to
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
scanf("%d%d%d",&x,&y,&z),addedge(x,y,z),addedge(n+y,n+x,z);
for (i=1;i<=n;++i) addedge(i,n+i,0);
for (i=1;i<=(n<<1);++i) dis[i]=INF; dis[1]=0;
hp.push(data(dis[1],1)); while (!hp.empty())
{
int now=hp.top().p; hp.pop(); if (vis[now]) continue; vis[now]=1;
for (i=head[now];i;i=e[i].nxt) if (dis[to]>dis[now]+e[i].v)
hp.push(data(dis[to]=dis[now]+e[i].v,to));
}
for (i=2;i<=n;++i) printf("%lld ",dis[n+i]!=INF?dis[n+i]:-1);
return 0;
}
C. Circular Mirror
首先不难发现存在直角三角形的充要条件是:存在某个颜色,其颜色点数大于等于\(3\)且其中的两点恰好在一直径上
首先我们可以扫一遍求出一共有多少对点对在一直径上,记为\(cnt\),剩下的自由点记为\(left=n-cnt*2\)
考虑枚举恰好有多少对直径点对的颜色相同,记为\(i\),那么此时的贡献即为:
\[C_{cnt}^i\times C_m^i\times i!\times [(m-i)\times (m-i-1)]^{cnt-i}\times (m-i)^{left} \]首先前面的\(C_{cnt}^i\times C_m^i\times i!\)表示从\(cnt\)个点对中选出\(i\)对并且找出\(i\)种颜色一一匹配的方案数,\([(m-i)\times (m-i-1)]^{cnt-i}\)表示剩余的\(cnt-i\)个点对的选法(此时两两颜色不同),\((m-i)^{left}\)表示剩下的自由点的选法
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=300005,mod=998244353;
int n,m,a[N],cnt,fac[N],ifac[N],ans; long long pfx[N],sum;
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
RI i; for (fac[0]=i=1;i<=n;++i) fac[i]=1LL*fac[i-1]*i%mod;
for (ifac[n]=quick_pow(fac[n]),i=n-1;~i;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
return 1LL*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=n;++i)
scanf("%d",&a[i]),pfx[i]=pfx[i-1]+a[i]; sum=pfx[n];
if (sum%2==0)
{
for (j=0,i=1;i<n;++i)
{
while (pfx[i]-pfx[j]>sum/2LL) ++j;
if (pfx[i]-pfx[j]==sum/2LL) ++cnt;
}
}
for (init(max(m,cnt)),i=0;i<=min(m,cnt);++i)
(ans+=1LL*C(cnt,i)*C(m,i)%mod*fac[i]%mod*quick_pow(1LL*(m-i)*(m-i-1)%mod,cnt-i)%mod*quick_pow(m-i,n-cnt*2)%mod)%=mod;
return printf("%d",ans),0;
}
H. Hot Black Hot White
昨天不小心漏看了这道过的人300+的题了,其实比H,C都简单啊(触发:构造题精通)
首先由于模\(3\)的性质可以转为求一个数每位上数字的和,因此\(\operatorname{concat}(x,y)\equiv(x+y)\mod 3\)
因此\(\operatorname{concat}(A_i, A_j) \times \operatorname{concat}(A_j, A_i) + A_i \times A_j \equiv (A_i+A_j)^2+A_i\times A_j \equiv A_i^2+A_j^2\mod 3\)
稍加讨论,我们发现\(A_i^2\bmod 3\)的值只可能是\(0/1\),一个naive的想法,令\(Z=0/2\),把所有\(A_i^2\bmod 3=0\)的数变白,把所有\(A_i^2\bmod 3=1\)的数变黑,即满足和不会反应的条件
那么这样不一定满足颜色的个数相等,因此我们讨论一下:
- 若\(A_i^2\bmod 3=0\)的数的个数更多,则令\(Z=2\),此时可以给\(A_i^2\bmod 3=0\)的一些数赋予另一种颜色而保持不会发生反应的特性
- 若\(A_i^2\bmod 3=1\)的数的个数更多,则令\(Z=0\),此时可以给\(A_i^2\bmod 3=1\)的一些数赋予另一种颜色而保持不会发生反应的特性
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,a[N],b[N],c[2],cnt;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (scanf("%d",&n),i=1;i<=n;++i)
scanf("%d",&a[i]),++c[b[i]=1LL*a[i]*a[i]%3];
if (c[0]>=c[1])
{
for (puts("2"),i=1;i<=n;++i) if (b[i]==1) putchar('1');
else if (b[i]==0&&cnt<n/2) putchar('0'),++cnt; else putchar('1');
} else
{
for (puts("0"),i=1;i<=n;++i) if (b[i]==0) putchar('0');
else if (b[i]==1&&cnt<n/2) putchar('1'),++cnt; else putchar('0');
}
return 0;
}
F. Field Photography
首先由于移动的次数不限,并且最后要满足或起来是一个数,我们考虑令\(k=\operatorname{LSB}(W_j)\),其中\(\operatorname{LSB}(x)\)表示\(x\)的二进制下从低位到高位第一个\(1\)的位置
不难发现由于或的性质,我们每次操作的步数必须是\(2^k\)的倍数,考虑以下一种移动方案:
- 每次对某一行向左或向右移动\(2^k\)的步数
- 最后将某一行先向右移动\(W_j\)的步数,然后再向左移动\(W_j\)的步数
不难发现这样一定是最优的(每次移动的单位长度最短)并且满足题目要求
不难发现此时我们只关心区间端点对\(2^k\)取模后的结果,具体的:
- 若\(r_i-l_i+1\ge 2^k\) ,则\([0,2^k)\)中的每个数都能被覆盖到
- 若\(r_i-l_i+1< 2^k\and l_i\bmod 2^k\le r_i\bmod 2^k\),则\([l_i\bmod 2^k,r_i\bmod 2^k]\)中的每个数都能被覆盖到
- 若\(r_i-l_i+1< 2^k\and l_i\bmod 2^k> r_i\bmod 2^k\),则\([l_i\bmod 2^k,2^k),[0,r_i\bmod 2^k]\)中的每个数都能被覆盖到
因此问题变为区间修改+全局最值,结果我这个傻狗去写线段树,然后常数太大T了,后来一想发现直接差分就好了
由于\(\operatorname{LSB}(x)\)的范围是\(\log W_j\)级别的,总复杂度\(O(n\log n\log W_j)\)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<utility>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
using namespace std;
const int N=100005;
typedef pair <int,int> pi;
int n,l[N],r[N],q,x,ans[35],cnt,sum; pi rst[N<<2];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&l[i],&r[i]);
for (j=0;j<=30;++j)
{
int k=1<<j; for (cnt=0,i=1;i<=n;++i)
if (r[i]-l[i]+1>=k) rst[++cnt]=mp(0,1),rst[++cnt]=mp(k,-1);
else if (l[i]%k<=r[i]%k) rst[++cnt]=mp(l[i]%k,1),rst[++cnt]=mp(r[i]%k+1,-1);
else rst[++cnt]=mp(l[i]%k,1),rst[++cnt]=mp(k,-1),rst[++cnt]=mp(0,1),rst[++cnt]=mp(r[i]%k+1,-1);
for (sort(rst+1,rst+cnt+1),sum=0,i=1;i<=cnt;++i)
sum+=rst[i].se,ans[j]=max(ans[j],sum);
}
for (scanf("%d",&q),i=1;i<=q;++i)
for (scanf("%d",&x),j=0;j<=30;++j)
if ((x>>j)&1) { printf("%d\n",ans[j]); break; }
return 0;
}
标签:cnt,const,14,int,bmod,Preliminary,Mirror,include,define 来源: https://www.cnblogs.com/cjjsb/p/16686695.html