AGC001
A. BBQ Easy
\(\rm sort\) 一遍后累加单数位即可
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=2020,mod=1e9+7,INF=1e9;
int n,l[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
n=read();
for(int i=1;i<=n*2;i++) l[i]=read();
sort(l+1,l+1+n*2);
int ans=0;
for(int i=1;i<=n;i++) ans+=l[i*2-1];
printf("%lld\n",ans);
return 0;
}
B. Mysterious Light
注意到一个美妙的性质:在反射的时候相当于做辗转相减法
于是我们认为最后答案一定和 \(\gcd\) 有关
手玩几组小数据后可知答案为 \(3(n-\gcd(n,x))\)
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=2020,mod=1e9+7,INF=1e9;
int n,x;
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
int gcd(int x,int y){
if(!y) return x;
return gcd(y,x%y);
}
signed main(){
n=read(),x=read();
printf("%lld\n",3ll*(n-gcd(n,x)));
return 0;
}
C. Shorten Diameter
我是sb才会把这玩意写挂
注意到 \(n\leqslant 2000\) 所以考虑 \(\mathcal{O}(n^2)\) 的做法就行
对于 \(k\) 为偶数的情况我们枚举直径中点
否则我们枚举直径中间的一条边
只要统计深度不大于 \((k+1)>>1\) 的点数就行
注意在 \(k\) 为奇数的时候一条边的一端可能会被另一端访问到,加个特判就行了
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<iostream>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=2002000,mod=1e9+7,INF=1e9;
struct Edge{
int pre,to;
}edge[WR<<1],rec[WR<<1];
int head[WR],tot;
int n,k;
int dpt[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
void add(int u,int v){
edge[++tot].pre=head[u];
edge[tot].to=v;
head[u]=tot;
}
int dfs1(int u,int root){
dpt[u]=dpt[root]+1;
if(dpt[u]>(k+1>>1)) return 0;
int cnt=1;
for(int i=head[u];i;i=edge[i].pre){
int v=edge[i].to;
if(v==root) continue;
cnt+=dfs1(v,u);
}
return cnt;
}
int dfs0(int u,int root){
dpt[u]=dpt[root]+1;
if(dpt[u]>(k>>1)+1) return 0;
int cnt=1;
for(int i=head[u];i;i=edge[i].pre){
int v=edge[i].to;
if(v==root) continue;
cnt+=dfs0(v,u);
}
return cnt;
}
signed main(){
n=read(),k=read();
int ans=INF;
for(int i=1;i<n;i++){
int u=read(),v=read();
rec[i].pre=u,rec[i].to=v;
add(u,v);add(v,u);
}
if(k&1){
for(int i=1;i<n;i++){
int u=rec[i].pre,v=rec[i].to;
int tmp=0;
dpt[v]=0,tmp+=dfs1(u,v);
dpt[u]=0,tmp+=dfs1(v,u);
ans=min(ans,n-tmp);
}
}else{
for(int i=1;i<=n;i++){
// cerr<<i<<endl;
ans=min(ans,n-dfs0(i,0));
}
}
printf("%lld\n",ans);
return 0;
}
D. Arrays and Palindrome
之前模拟赛考过了
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=5001000;
int n,m;
int a[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
m=read(),n=read();
int cnt=0;
for(int i=1;i<=n;i++)cnt+=(a[i]=read())&1;
if(m<=2){
for(int i=1;i<=n;i++)printf("%lld ",a[i]);
printf("\n1\n%lld\n",m);
return 0;
}
if(n==1){
printf("%lld\n2\n%lld %lld\n",m,m-1,1ll);
return 0;
}
if(cnt>2){puts("Impossible");return 0;}
for(int i=1;i<=n;i++)
if(a[i]&1){swap(a[1],a[i]);break;}
for(int i=n;i>=2;i--)
if(a[i]&1){swap(a[n],a[i]);break;}
for(int i=1;i<=n;i++)printf("%lld ",a[i]);puts("");
printf("%lld\n",n-(a[1]==1));
if(a[1]>1)printf("%lld ",a[1]-1);
for(int i=2;i<n;i++)printf("%lld ",a[i]);
printf("%lld\n",a[n]+1);
return 0;
}
E. BBQ Hard
一个比较套路的 trick
原题就等同于求 \(\large\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\binom{a_i+b_i+a_j+b_j}{a_i+a_j}\)
然后我们考虑其组合意义,认为它是从 \((-a_i,-b_i)\) 走到 \((a_j,b_j)\) 的路径条数
然后注意到 \(a_i\) 的数据范围较小(不小也没关系我们可以离散化)于是硬 \(\rm dp\) 即可
最后要减掉一部分 \(i=j\) 的情况再除以 \(2\)
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<iostream>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=500100,mod=1e9+7,INF=1e9;
int a[WR],b[WR];
int dp[4040][4040];
int f[WR],inv[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
int quick_pow(int a,int b){
int base=a,res=1;
while(b){
if(b&1) res=res*base%mod;
base=base*base%mod;
b>>=1;
}
return res;
}
int C(int n,int m){
return f[n]*inv[m]%mod*inv[n-m]%mod;
}
int n;
signed main(){
n=read();
f[0]=inv[0]=1;
for(int i=1;i<WR;i++) f[i]=f[i-1]*i%mod;
inv[WR-1]=quick_pow(f[WR-1],mod-2);
for(int i=WR-2;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
int base=2020;
for(int i=1;i<=n;i++){
a[i]=read(),b[i]=read();
dp[base-a[i]][base-b[i]]++;
}
for(int i=1;i<4040;i++){
for(int j=1;j<4040;j++){
dp[i][j]=(dp[i][j]+dp[i-1][j]+dp[i][j-1])%mod;
}
}
int ans=0;
for(int i=1;i<=n;i++){
ans=(ans+dp[base+a[i]][base+b[i]])%mod;
}
for(int i=1;i<=n;i++){
ans=(ans-C(2*(a[i]+b[i]),2*a[i])+mod)%mod;
}
ans=ans*quick_pow(2ll,mod-2)%mod;
while(ans<0) ans+=mod;
printf("%lld\n",ans);
return 0;
}
F. Wide Swap
没想出来,贺的
有一个奇妙的转化:
令 \(b[a[i]]=i\),那么操作就变成了交换相邻两个差的绝对值 \(>=k\) 的位置。
可以发现最小化 \(b\) 的字典序相当于最小化 \(a\) 的字典序
归并排序扫一遍就行了
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<iostream>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=500100,mod=1e9+7,INF=1e9;
int n,k;
int a[WR],b[WR],minn[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
void merge(int l,int r){
if(l==r) return;
int mid=(l+r)>>1;
merge(l,mid);
merge(mid+1,r);
minn[mid]=a[mid];
for(int i=mid-1;i>=l;i--) minn[i]=min(minn[i+1],a[i]);
int i=l,j=mid+1,pos=l;
while(i<=mid&&j<=r){
if(minn[i]-a[j]>=k) b[pos]=a[j],j++;
else b[pos]=a[i],i++;
pos++;
}
while(i<=mid) b[pos]=a[i],pos++,i++;
while(j<=r) b[pos]=a[j],pos++,j++;
for(i=l;i<=r;i++) a[i]=b[i];
}
signed main(){
n=read(),k=read();
for(int i=1;i<=n;i++){
int pos=read();
a[pos]=i;
}
merge(1,n);
for(int i=1;i<=n;i++) b[a[i]]=i;
for(int i=1;i<=n;i++) printf("%lld\n",b[i]);
return 0;
}
AGC002
A. Range Product
小分讨即可
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<iostream>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=500100,mod=1e9+7,INF=1e9;
int a,b;
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
a=read(),b=read();
if(a<=0&&b>=0) printf("Zero\n");
else if(a>0) printf("Positive\n");
else{
int len=b-a+1;
if(len&1) printf("Negative\n");
else printf("Positive\n");
}
return 0;
}
B. Box and Ball
第一眼还以为是概率期望吓死我了。。。
记录一下每个盒子里的球个数,只要盒子里还有球且被 \(1\) 转移过就可以让它是红球
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<iostream>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=500100,mod=1e9+7,INF=1e9;
int n,m;
int cnt[WR];
bool tag[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
n=read(),m=read();
tag[1]=true;
for(int i=1;i<=n;i++) cnt[i]=1;
for(int i=1;i<=m;i++){
int x=read(),y=read();
tag[y]|=tag[x];
cnt[x]--,cnt[y]++;
if(!cnt[x]) tag[x]=false;
}
int ans=0;
for(int i=1;i<=n;i++){
if(tag[i]) ans++;
}
printf("%lld\n",ans);
return 0;
}
C. Knot Puzzle
注意到只要有两条绳子长度加起来大于 \(l\) 就合法,否则肯定无解
做法就显然了
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<iostream>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=500100,mod=1e9+7,INF=1e9;
int n,l;
int a[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
n=read(),l=read();
for(int i=1;i<=n;i++) a[i]=read();
int maxx=0,pos=0;
for(int i=2;i<=n;i++){
if(a[i]+a[i-1]>maxx){
maxx=a[i]+a[i-1];
pos=i-1;
}
}
if(maxx<l) printf("Impossible\n");
else{
printf("Possible\n");
for(int i=1;i<pos;i++) printf("%lld\n",i);
for(int i=n-1;i>pos;i--) printf("%lld\n",i);
printf("%lld\n",pos);
}
return 0;
}
D. Stamp Rally
我淦这个二分调起来真的离谱
轻轻松松就T掉也是没谁了
首先这道题很明显就是 \(\rm Kruscal\) 重构树
然后建一个 \(\rm ST\) 表,对答案二分
注意到重构树有一个美妙的性质,它的边权是单调的
所以二分是可行的
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<iostream>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=500100,mod=1e9+7,INF=1e9;
struct Edge{
int pre,to;
}edge[WR<<1];
int head[WR],tot;
int n,m;
int fa[WR],val[WR];
int fth[WR][20],son[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
int getfa(int x){
if(fa[x]==x) return x;
return fa[x]=getfa(fa[x]);
}
void add(int u,int v){
edge[++tot].pre=head[u];
edge[tot].to=v;
head[u]=tot;
}
void dfs(int u,int root){
fth[u][0]=root;
for(int i=1;i<20;i++) fth[u][i]=fth[fth[u][i-1]][i-1];
// cerr<<"head="<<head[u]<<" "<<u<<endl;
if(!head[u]) son[u]=1;
for(int i=head[u];i;i=edge[i].pre){
int v=edge[i].to;
dfs(v,u);
son[u]+=son[v];
}
}
int check(int mid,int x,int y){
for(int i=19;i>=0;i--){
if(val[fth[x][i]]<=mid) x=fth[x][i];
if(val[fth[y][i]]<=mid) y=fth[y][i];
}
if(x==y) return son[x];
else return son[x]+son[y];
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n*2;i++) fa[i]=i;
int cnt=n;val[0]=INF;
for(int i=1;i<=m;i++){
int u=read(),v=read();
int fau=getfa(u),fav=getfa(v);
if(fau==fav) continue;
fa[fau]=fa[fav]=++cnt;
val[cnt]=i;
add(cnt,fau);add(cnt,fav);
}
dfs(cnt,0);
// for(int i=1;i<=cnt;i++) printf("%lld %lld %lld\n",i,val[i],son[i]);
int Q=read();
while(Q--){
int x=read(),y=read(),z=read();
int l=0,r=m,ans=m;
while(l<=r){
// cerr<<l<<" "<<r<<endl;
int mid=(l+r)>>1;
if(check(mid,x,y)<z) l=mid+1,ans=mid+1;
else r=mid-1;
}
// while(check(ans,x,y)>=z) ans--;
// while(check(ans,x,y)<z) ans++;
printf("%lld\n",ans);
}
return 0;
}
E. Candy Piles
手动捏一组样例
4 6 5 6 3 1 4
然后将其排序
6 6 5 4 4 3 1
不妨可视化
00
000
00000
000000
000000
0000000
显然地,边缘点是必败点
不难发现每堆删掉一个和删掉最多的一堆就代表着向右边或上边走一步
然后如果一个点右边和上边都是必败点这个点就是必胜点
所以原图转化如下(用1
表示必胜点)
00
100
00100
010010
100100
0010010
不难发现原图按对角线排列
所以只要统计一个左下角为原点的最大正方形右上端是什么即可
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=500100,mod=1e9+7,INF=1e9;
int n,a[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
bool cmp(int x,int y){
return x>y;
}
signed main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++){
if(a[i+1]<i+1){
int cnt=0;
while(a[i+1+cnt]==i) cnt++;
if(((a[i]-i)&1)||(cnt&1)) printf("First\n");
else printf("Second\n");
break;
}
}
return 0;
}
F. Leftmost Ball
完全不知道怎么去重
于是贺了题解
觉得这个去重的方法很巧妙
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=2040,mod=1e9+7,INF=1e9;
int n,k;
int f[WR*WR],inv[WR*WR];
int dp[WR][WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
int quick_pow(int a,int b){
int base=a,res=1;
while(b){
if(b&1) res=res*base%mod;
base=base*base%mod;
b>>=1;
}
return res;
}
int C(int n,int m){
if(n<m) return 0;
return f[n]*inv[m]%mod*inv[n-m]%mod;
}
signed main(){
n=read(),k=read();
f[0]=inv[0]=1;
if(k==1){
printf("1\n");
return 0;
}
for(int i=1;i<WR*WR;i++) f[i]=f[i-1]*i%mod;
inv[WR*WR-1]=quick_pow(f[WR*WR-1],mod-2);
for(int i=WR*WR-2;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
for(int i=1;i<=n;i++) dp[i][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i][j]=(dp[i][j]+dp[i-1][j]+dp[i][j-1]*(n-j+1)%mod*C(n*k-i-(j-1)*(k-1)-1,k-2)%mod)%mod;
}
}
printf("%lld\n",dp[n][n]);
return 0;
}
AGC003
A. Wanna go back home
统计是否都出现过或者只出现相反的两个方向即可
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=2040,mod=1e9+7,INF=1e9;
char str[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
scanf("%s",str+1);
int n=strlen(str+1);
bool tagN=false,tagW=false,tagS=false,tagE=false;
for(int i=1;i<=n;i++){
if(str[i]=='N') tagN=true;
if(str[i]=='W') tagW=true;
if(str[i]=='S') tagS=true;
if(str[i]=='E') tagE=true;
}
if(tagN&&tagS&&tagE&&tagW) printf("Yes\n");
else if(tagN&&tagS&&(!tagE)&&(!tagW)) printf("Yes\n");
else if(tagE&&tagW&&(!tagS)&&(!tagN)) printf("Yes\n");
else printf("No\n");
return 0;
}
B. Simplified mahjong
SB贪心题
显然优先和自己配对即可
为啥我挂了 \(4\) 发?这我不好说
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=200040,mod=1e9+7,INF=1e9;
int n,ans;
int a[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++){
ans+=a[i]/2;
a[i]%=2;
if(i<n&&a[i]&&a[i+1]) ans++,a[i]--,a[i+1]--;
}
printf("%lld\n",ans);
return 0;
}
C. BBuBBBlesort!
显然地,我们可以通过一定的操作 \(2\) 将所有的奇数位、偶数位排好序
但是考虑到有些数本来应该在奇数/偶数位上但却不在,怎么办呢?
可以通过一些交换 \(2\) 将其变为相邻(显然)然后用一次操作 \(1\)
答案显然就是不在原本奇偶性位置上的数的个数除以 \(2\)
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=200040,mod=1e9+7,INF=1e9;
int n,ans;
int a[WR],rec[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
n=read();
for(int i=1;i<=n;i++) a[i]=rec[i]=read();
sort(rec+1,rec+1+n);
int tot=unique(rec+1,rec+1+n)-rec-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(rec+1,rec+1+tot,a[i])-rec;
for(int i=1;i<=n;i++){
if((a[i]&1)!=(i&1)) ans++;
}
printf("%lld\n",ans>>1);
return 0;
}