AtCoder Grand Contest 034
一直很想找几套数字有特殊意义的考试题做做,终于找到时间了。
其实个人认为\(Atcoder\)上的题目比较思维化,有点意思……(主要是比\(Codeforces\)好看)
Atcoder Grand Contest 034 entry
\(\text{A - Kenken Race}\)
考虑两种情况:
\(1.\)若\(a<b\quad c<d\),则只要分别可以到达即可。
\(2.\)否则,只需在\(b-1\)到\(d+1\)的路上有长度大于等于\(3\)的一串空格即可。
代码如下,实现简单,思路简单:
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,a,b,c,d,p[maxn];
int f1[maxn],f2[maxn];
int main(){
n=read();
a=read(),b=read();
c=read(),d=read();
for(int i=1;i<=n;i++)
p[i]=getchar()=='.';
f1[a]=f2[b]=1;
for(int i=a+1;i<=c;i++)
f1[i]=(p[i]&(f1[i-1]|f1[i-2]));
for(int i=b+1;i<=d;i++)
f2[i]=(p[i]&(f2[i-1]|f2[i-2]));
if(!f1[c]||!f2[d]||c==d)return puts("No"),0;
if(c<d)return puts("Yes"),0;
int cnt=0;
for(int i=b-1;i<=d+1;i++)
if(!p[i]){if(cnt>=3)return puts("Yes"),0;cnt=0;}
else cnt++;
if(cnt>=3)puts("Yes");
else puts("No");
return 0;
}
正好\(34\)行呵(真不戳)。
\(\text{B - ABC}\)
个人觉得样例\(1\)的提示性有点强。
结论就是一个\(ABC\)的贡献是其前导\(A\)的个数,操作过程样例\(1\)解释都给了。
那么简单维护一下前导\(A\)统计答案即可,记得开\(long long\)。
代码如下,仅供参考:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
int n,cnt,pos,tot;
char s[maxn];
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
signed main(){
scanf("%s",s+1);
n=strlen(s+1),pos=1;
while(pos<=n){
//printf("%d %d %d\n",pos,cnt,tot);
if(s[pos]=='A'&&s[pos+1]=='B'&&s[pos+2]=='C'){
s[pos+2]='A';cnt+=1+tot;pos+=2;
}else if(s[pos]=='A')tot++,pos++;
else tot=0,pos++;
}
printf("%lld\n",cnt);
return 0;
}
\(\text{C - Tests}\)
二分答案,利用一次函数的单调性,枚举。
时间复杂度\(\Theta(nlogn)\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int mod=1e9+7;
int n,k,sum;
struct node{int l,r,b,f;}p[maxn];
inline int cmp(node x,node y){return x.f>y.f;}
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int pre[maxn];
inline int calc(int id,int x){
if(x<=p[id].b)return x*p[id].l;
return p[id].b*p[id].l+p[id].r*(x-p[id].b);
}
inline bool check(int val){
int t=val/k,q=val%k;
int res=0;
for(int i=1;i<=n;i++)
if(i<=t)res=max(res,pre[t+1]-p[i].f+calc(i,q));
else res=max(res,pre[t]+calc(i,q));
return res>=sum;
}
signed main(){
n=read(),k=read();
for(int i=1;i<=n;i++){
p[i].b=read();
p[i].l=read(),p[i].r=read();
sum+=p[i].b*p[i].l;
p[i].f=calc(i,k);
}
sort(p+1,p+1+n,cmp);
int l=0,r=0;
for(int i=1;i<=n;i++)
r+=p[i].b;
for(int i=1;i<=n;i++)
pre[i]=pre[i-1]+p[i].f;
int ans=r;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))ans=mid,r=mid-1;
else l=mid+1;
}
printf("%lld\n",ans);
return 0;
}
\(\text{D - Manhattan Max Matching}\)
显然使用费用流,但朴素建边会\(T\)飞,考虑建虚点优化建边。
显然如果要将\(a(x_a,y_a)\)上的点与\(b(x_b,y_b)\)上的点配对的话,其代价为\(dis_{a,b}=|a_x-b_x|+|a_y-b_y|\)
这个式子有四种可能,如下:
\(a_x-b_x+a_y-b_y\quad\)即\(a_x+a_y\)和\(-b_x-b_y\)
\(a_x-b_x+b_y-a_y\quad\)即\(a_x-a_y\)和\(-b_x+b_y\)
\(b_x-a_x+a_y-b_y\quad\)即\(-a_x+a_y\)和\(b_x-b_y\)
\(b_x-a_x+b_y-a_y\quad\)即\(-a_x-a_y\)和\(b_x+b_y\)
发现\(dis\)即这四个值中的最大值,也是最小值的相反数。
故我们建四个虚点,分别连每个点的\(x+y\),\(x-y\),\(-x+y\),\(-x-y\)。
跑最小费用最大流时一定会走两点欧几里得距离的相反数。
故跑一遍费用流,输出最小费用的相反数即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 0x7f
const int maxn=1e5+100;
int beg[maxn],nex[maxn],to[maxn],f[maxn],c[maxn],e,mf,mc;
void add(int x,int y,int z,int d){
nex[e]=beg[x];beg[x]=e;
to[e]=y;f[e]=z;c[e]=d;e++;
}
int n,m,st,ed,dis[maxn],vis[maxn],pre[maxn],val[maxn],flow[maxn];
queue<int>q;
int spfa(){
memset(dis,inf,sizeof(dis));
memset(flow,inf,sizeof(flow));
memset(vis,0,sizeof(vis));
dis[st]=0;pre[ed]=-1;vis[st]=1;
q.push(st);
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=0;
for(int i=beg[x];~i;i=nex[i]){
int t=to[i];
if(f[i]&&dis[t]>dis[x]+c[i]){
dis[t]=dis[x]+c[i];
pre[t]=x;val[t]=i;
flow[t]=min(flow[x],f[i]);
if(!vis[t]){
vis[t]=1;
q.push(t);
}
}
}
}
return pre[ed]!=-1;
}
signed main(){
cin>>n;
int x,y,z,p1,p2,p3,p4;
memset(beg,-1,sizeof(beg));
st=n+n+1;ed=st+1;
p1=ed+1,p2=p1+1,p3=p2+1,p4=p3+1;
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&x,&y,&z);
add(st,i,z,0);add(i,st,0,0);
add(i,p1,inf,x+y);add(p1,i,0,-x-y);
add(i,p2,inf,x-y);add(p2,i,0,y-x);
add(i,p3,inf,y-x);add(p3,i,0,x-y);
add(i,p4,inf,-x-y);add(p4,i,0,x+y);
}
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&x,&y,&z);
add(i+n,ed,z,0);add(ed,i+n,0,0);
add(p1,i+n,inf,-x-y);add(i+n,p1,0,x+y);
add(p2,i+n,inf,y-x);add(i+n,p2,0,x-y);
add(p3,i+n,inf,x-y);add(i+n,p3,0,y-x);
add(p4,i+n,inf,x+y);add(i+n,p4,0,-x-y);
}
while(spfa()){
int now=ed;
mf+=flow[ed];
mc+=flow[ed]*dis[ed];
while(now!=st){
f[val[now]]-=flow[ed];
f[val[now]^1]+=flow[ed];
now=pre[now];
}
}
printf("%lld\n",-mc);
return 0;
}
\(\text{E - Complete Compress}\)
神仙题,居然在冬令营上被讲到了。
考虑建成一个集合互相抵消的模。
我们要将这些集合最终抵消完,考虑最大的集合。
若这个集合的数量小于等于半数,则可以消集合大小的半数次,即\([\frac{size}{2}]\)。
若这个集合的数量大于半数,则用外部的消完后本集合内再相互抵消。
每个点做一遍树形\(dp\),复杂度\(\Theta(n^2)\)
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e3+10;
const int mod=1e9+7;
int n,a[maxn],cnt;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
string s;
int beg[maxn],nex[maxn<<1],to[maxn<<1],e;
inline void add(int x,int y){
e++;nex[e]=beg[x];
beg[x]=e;to[e]=y;
}
int val[maxn];
int dis[maxn],f[maxn],ans=inf;
inline void solve(int x,int fa){
val[x]=a[x];dis[x]=0;
int son=0;
for(int i=beg[x];i;i=nex[i]){
int t=to[i];
if(t==fa)continue;
solve(t,x);
val[x]+=val[t];
dis[x]+=(dis[t]+=val[t]);
if(dis[t]>dis[son])son=t;
}
if(!son)return void(f[x]=0);
if(dis[x]>=2*dis[son])f[x]=dis[x]/2;
else f[x]=dis[x]-dis[son]+min(f[son],(dis[son]*2-dis[x])/2);
}
int main(){
n=read();cin>>s;
for(int i=1;i<=n;i++)
if(s[i-1]=='1')a[i]=1,cnt++;
int x,y;
for(int i=1;i<n;i++){
x=read(),y=read();
add(x,y),add(y,x);
}
for(int i=1;i<=n;i++){
solve(i,0);
if(!(dis[i]&1)&&f[i]==dis[i]/2)
ans=min(ans,dis[i]/2);
}
printf("%d\n",ans==inf?-1:ans);
return 0;
}
\(\text{F - RNG and XOR}\)
神仙\(fwt\),基本不可做,操作仙得一\(mp\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=(1<<18)+10;
const int mod=998244353;
const int inv2=(mod+1)/2;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
inline int ksm(int x,int y){
int res=1;
while(y){
if(y&1)res=res*x%mod;
x=x*x%mod;y>>=1;
}
return res;
}
int n,sum,len,p[maxn],q[maxn],f[maxn];
inline void fwt(int *a,int n,int flag){
for(int i=2;i<=n;i<<=1)
for(int j=0,p=i>>1;j<n;j+=i)
for(int k=j;k<j+p;k++){
int x=a[k],y=a[k+p];
a[k]=(x+y)%mod;
a[k+p]=(x+mod-y)%mod;
if(!flag){
a[k]=a[k]*inv2%mod;
a[k+p]=a[k+p]*inv2%mod;
}
}
}
signed main(){
n=read();len=1<<n;
for(int i=0;i<len;i++){
p[i]=read();
sum=(sum+p[i])%mod;
}
sum=ksm(sum,mod-2);
for(int i=0;i<len;i++)
p[i]=p[i]*sum%mod;
q[0]=len-1;p[0]+=mod-1;
for(int i=1;i<len;i++)
q[i]=mod-1;
fwt(p,len,1);fwt(q,len,1);
for(int i=0;i<len;i++)
f[i]=q[i]*ksm(p[i],mod-2)%mod;
fwt(f,len,0);
int tmp=mod-f[0];
for(int i=0;i<len;i++)
printf("%lld\n",(f[i]+tmp)%mod);
return 0;
}