2022 拉丁美洲区域赛 vp
B(贪心、fft)
将两数列排序后按照正负分离,最大值与最小值类似
考虑求最大值,对于两数列中同号的部分,显然按照绝对值顺序依次相乘;对于异号部分,需要令绝对值乘积尽可能小,简单列式子后可以得到该部分贡献为倒序卷积,按照不同情况翻转其中一个数列
最小值同理,只需先将其中一个数列翻转,后续步骤类似。
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
const int MAXN=200100;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
const ld pi=acosl(-1);
int n,a[MAXN],b[MAXN],g[MAXN],h[MAXN];
ll ansmx[MAXN],ansmn[MAXN];
namespace Poly
{
struct Cd{ld x,y;Cd(ld x=0,ld y=0):x(x),y(y){}};
Cd operator + (Cd a,Cd b){return Cd(a.x+b.x,a.y+b.y);}
Cd operator - (Cd a,Cd b){return Cd(a.x-b.x,a.y-b.y);}
Cd operator * (Cd a,Cd b){return Cd(a.x*b.x-a.y*b.y,a.y*b.x+b.y*a.x);}
Cd operator / (Cd a,ld b){return Cd(a.x/b,a.y/b);}
int rev[MAXN<<2];
int mem(int n)
{
int lg=0,lim=1;for(;lim<n;lim<<=1,lg++);
rep(i,0,lim-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
return lim;
}
void fft(Cd *a,int n,int f)
{
rep(i,0,n-1) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1)
{
Cd wn(cosl(pi/i),f*sinl(pi/i));
for(int j=0;j<n;j+=i<<1)
{
Cd w(1,0),x,y;
for(int k=0;k<i;k++,w=w*wn)
x=a[k+j],y=a[k+j+i]*w,a[j+k]=x+y,a[j+k+i]=x-y;
}
}
if(f==1) return ;rep(i,0,n-1) a[i]=a[i]/n;
}
void Mul(ll *res,int *a,int n,int *b,int m)
{
static Cd A[MAXN<<2],B[MAXN<<2];
int s=mem(n+m-1);
rep(i,0,n-1) A[i]=Cd(a[i],0);
rep(i,0,m-1) B[i]=Cd(b[i],0);
fill(A+n,A+s,Cd());fill(B+m,B+s,Cd());
fft(A,s,1);fft(B,s,1);
rep(i,0,s-1) A[i]=A[i]*B[i];
fft(A,s,-1);
rep(i,0,n+m-1) res[i]=(ll)roundl(A[i].x);
}
}
ll mulres[MAXN<<2];
void solve_max()
{
rep(i,1,n) a[i]=g[i],b[i]=h[i];
int l=1,r=n;
vi res;
for(;a[l]<0&&b[l]<0;l++) res.push_back(a[l]*b[l]);
for(;a[r]>0&&b[r]>0;r--) res.push_back(a[r]*b[r]);
sort(res.begin(),res.end());
reverse(res.begin(),res.end());
int tot=0;
for(auto x:res) ansmx[1+tot]=x+ansmx[tot],tot++;
if((a[l]==0&&a[r]==0)||(b[l]==0&&b[r]==0)) return ;
if(b[l]<0||a[r]>0) reverse(b+l,b+r+1);
else reverse(a+l,a+r+1);
Poly::Mul(mulres,a+l,r-l+1,b+l,r-l+1);
rep(i,0,r-l) ansmx[tot+i+1]=ansmx[tot]+mulres[i];
//rep(i,1,n) cout<<ansmx[i]<<" ";puts("");
}
void solve_min()
{
rep(i,1,n) a[i]=g[i],b[i]=h[i];
reverse(b+1,b+n+1);
int l=1,r=n;
vi res;
for(;a[l]<0&&b[l]>0;l++) res.push_back(a[l]*b[l]);
for(;a[r]>0&&b[r]<0;r--) res.push_back(a[r]*b[r]);
sort(res.begin(),res.end());
int tot=0;
for(auto x:res) ansmn[1+tot]=x+ansmn[tot],tot++;
if((a[l]==0&&a[r]==0)||(b[l]==0&&b[r]==0)) return ;
if(a[r]>0||b[l]>0) reverse(b+l,b+r+1);
else reverse(a+l,a+r+1);
Poly::Mul(mulres,a+l,r-l+1,b+l,r-l+1);
rep(i,0,r-l) ansmn[tot+i+1]=ansmn[tot]+mulres[i];
//rep(i,1,n) cout<<ansmn[i]<<" ";puts("");
}
int main()
{
n=read();
rep(i,1,n) g[i]=read();
rep(i,1,n) h[i]=read();
sort(g+1,g+n+1);
sort(h+1,h+n+1);
solve_max();
solve_min();
rep(i,1,n) printf("%lld %lld\n",ansmn[i],ansmx[i]);
}
F(并查集)
由于权值为2的幂次,需要将\(n\)点与其余点尽可能分离,只需在删去\(n\)点后选取包含\(n-1\)点的联通块
实现时在连边时跳过\(n\)点即可
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
const int MAXN=200100;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n,m,fa[MAXN];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int merge(int x,int y)
{
int f1=find(x),f2=find(y);
if(f1!=f2) return fa[f1]=f2;
return 0;
}
int main()
{
n=read(),m=read();
rep(i,1,n) fa[i]=i;
while(m--)
{
int a=read(),b=read();
if(a==n||b==n) continue;
merge(a,b);
}
rep(i,1,n) putchar(find(i)==find(n-1)?'B':'A');
}
G(树哈希)
对所有无根树做树哈希,存在size大小的哈希表里
对每颗树首先枚举因数\(d\),从叶子判断是否可以被划分为若干大小为\(d\)的联通块,对每个块计算哈希值判断是否全部相同,若相同则加上哈希表里该哈希值的个数
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int MAXN=200100;
const int inf=1e9;
const int mod1=998244353,mod2=1000000009;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n,pri[MAXN<<1],tot;
bool ntp[MAXN*15];
void sieve(int n=3e6)
{
rep(i,2,n)
{
if(!ntp[i]) pri[++tot]=i;
for(int j=1;j<=tot&&i*pri[j]<=n;++j)
{ntp[i*pri[j]]=1;if(i%pri[j]==0) break;}
}
}
vi G[MAXN];
vector<pii> trs[MAXN];
int sz[MAXN],Sz,Mx,ctr[2],vis[MAXN];
unordered_map<ull,int> hsh[MAXN];
void getrt(int x,int pa)
{
int mxs=0;sz[x]=1;
for(auto v:G[x]) if(v!=pa&&!vis[v])
{
getrt(v,x);
mxs=max(mxs,sz[v]),sz[x]+=sz[v];
}
mxs=max(mxs,Sz-sz[x]);
if(mxs*2<=Sz) ctr[ctr[0]!=0]=x;
}
ull dfs(int x,int pa)
{
ll v1=1,v2=1;sz[x]=1;
for(auto v:G[x]) if(v!=pa&&!vis[v])
{
auto w=dfs(v,x);
sz[x]+=sz[v];
ll a=w>>32,b=w&INT_MAX;
v1=(a*pri[sz[v]]+v1)%mod1,
v2=(b*pri[sz[v]]+v2)%mod2;
}
//cout<<x<<" "<<v1<<" "<<v2<<endl;
return (v1<<32)|v2;
}
ull gethash(int tot)
{
Sz=Mx=tot,ctr[0]=ctr[1]=0;
getrt(1,0);
return dfs(ctr[0],0);
}
int flg=0,blksz,ans;
ull v1,v2;
void dfsc(int x,int pa)
{
sz[x]=1;
for(auto v:G[x]) if(v^pa)
{
dfsc(v,x);
sz[x]+=sz[v];
}
if(sz[x]>blksz) {flg=1;return ;}
if(sz[x]==blksz)
{
vis[pa]=1;
Sz=Mx=blksz,ctr[0]=ctr[1]=0;
getrt(x,pa);
if(!v1)
{
v1=dfs(ctr[0],0);
if(ctr[1]) v2=dfs(ctr[1],0);
}
else if(!v2)
{
if(ctr[1]||dfs(ctr[0],0)!=v1) flg=1;
}
else
{
if(!ctr[1]) flg=1;
ull w1=dfs(ctr[0],0),w2=dfs(ctr[1],0);
if((w1!=v1||w2!=v2)&&(w1!=v2||w2!=v1)) flg=1;
}
//cout<<"dfsc: "<<x<<" "<<v1<<" "<<v2<<endl;
if(flg) return ;
vis[x]=1,vis[pa]=0,sz[x]=0;
}
}
void check(int tot,int blk)
{
flg=0,blksz=blk,v1=v2=0;
rep(i,1,tot) vis[i]=0;
dfsc(1,0);
//cout<<"blk: "<<blk<<" "<<flg<<" "<<v1<<" "<<v2<<endl;
if(flg) return ;
if(!v2||v1==v2) ans+=hsh[blk][v1];
else ans+=hsh[blk][v1]+hsh[blk][v2];
}
int main()
{
sieve();
n=read();
rep(i,1,n)
{
int m=read(),a,b;
rep(j,2,m)
{
a=read(),b=read();
trs[i].emplace_back(a,b);
G[a].push_back(b),G[b].push_back(a);
}
ull w=gethash(m);
//cout<<m<<" "<<w<<" "<<(w>>32)<<" "<<(w&INT_MAX)<<endl;
hsh[m][w]++;
rep(i,1,m) G[i].clear();
}
rep(i,1,n)
{
int m=trs[i].size()+1;
for(auto [x,y]:trs[i]) G[x].push_back(y),G[y].push_back(x);
//cout<<"i: "<<i<<"\n";
ans=-1;
rep(blk,1,m) if(m%blk==0)
check(m,blk);
rep(i,1,m) G[i].clear();
printf("%d%c",ans,i==n?'\n':' ');
}
}
H(KM)
容易算出每个点放在每个位置的贡献,即\(dis[i][j]\),使用KM计算最小匹配即可
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
#define Fill(x,a) memset(x,a,sizeof(x))
const int MAXN=510;
const ll inf=1e18;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
namespace KM
{
int n,vx[MAXN],vy[MAXN],linky[MAXN],pre[MAXN];
ll slack[MAXN],dbx[MAXN],dby[MAXN],dis[MAXN][MAXN],ans;
void bfs(int k)
{
int py=0,px,yy=0;ll tmp;
linky[0]=k;
Fill(slack,0x3f);Fill(pre,0);
do
{
px=linky[py],tmp=inf,vy[py]=1;
rep(i,1,n) if(!vy[i])
{
if(dbx[px]+dby[i]-dis[px][i]<slack[i])
slack[i]=dbx[px]+dby[i]-dis[px][i],pre[i]=py;
if(slack[i]<tmp) tmp=slack[i],yy=i;
}
rep(i,0,n)
if(vy[i]) dbx[linky[i]]-=tmp,dby[i]+=tmp;
else slack[i]-=tmp;
py=yy;
}while(linky[py]);
while(py) linky[py]=linky[pre[py]],py=pre[py];
}
ll solve()
{
Fill(linky,0);rep(i,1,n) {Fill(vy,0);bfs(i);}
ans=0;rep(i,1,n) ans+=dis[linky[i]][i];
return ans;
}
};
int n,e[MAXN][MAXN];
int main()
{
n=read();KM::n=(n+1)/2;
rep(i,1,n) rep(j,1,n) e[i][j]=read();
rep(i,1,(n+1)/2)
{
KM::dbx[i]=-inf;
rep(j,1,(n+1)/2)
KM::dis[i][j]=-e[i*2-1][j*2-2]-e[i*2-1][j*2],
KM::dbx[i]=max(KM::dbx[i],KM::dis[i][j]);
}
ll ans=KM::solve();
printf("%lld\n",-ans);
}
I(模拟)
按题意分类模拟
#include<bits/stdc++.h>
using namespace std;
char tmp[111];
int n, dn;
int main()
{
int i;
scanf("%s%d", tmp, &n);
if (tmp[0] == 'M')
{
dn = 0;
}
else if (tmp[0] == 'T')
{
if (tmp[1] == 'u')
{
dn = 1;
}
else
{
dn = 3;
}
}
else if (tmp[0] == 'W')
{
dn = 2;
}
else if (tmp[0] == 'F')
{
dn = 4;
}
else if (tmp[1] == 'a')
{
dn = 5;
}
else
{
dn = 6;
}
int minans = 100000000;
for (i=1; i<=n; ++i)
{
int t, t1, t2, t3;
scanf("%d", &t);
{
t1 = t;
int predn = (dn - t1%7 + 7) % 7;
while (predn % 2 == 1 && t1 >= 30)
{
predn += 2;
t1 -= 30;
if (predn == 5)
{
t1 -= 2;
predn = 0;
break;
}
}
if (t1 >= 0)
{
t1 %= 91;
}
while (t1 >= 30)
{
predn = (predn + 2)% 7;
t1 -= 30;
if (predn == 6)
{
t1 -= 1;
predn = 0;
}
}
}
if (t1 < 0)
{
t3 = - t1;
}
else
{
t3 = (t1 == 0 && t != 0) ? 0 : 30 - t1;
t2 = dn + t3;
if (t2 %7 == 5) t3 += 2;
else if (t2 %7 == 6) t3 += 1;
}
minans = min(minans, t3);
}
printf("%d\n", minans);
return 0;
}
J(栈)
容易发现仅有两端点都在边界上的点对才有意义,否则均可以绕边框
相当于在环上不能存在\(ABAB\)型的点对,随意找一个起点check即可
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int MAXN=200100;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int w,h,n,tot,tp,st[MAXN];
pii g[MAXN];
int calc(int x,int y)
{
if(x==0&&y>=0) return y+1;
if(y==h&&x>=0) return x+h+1;
if(x==w) return h+1+w+(h-y);
if(y==0) return h*2+w+1+(w-x);
return 0;
}
int main()
{
w=read(),h=read(),n=read();
rep(i,1,n)
{
int x=read(),y=read(),a=read(),b=read();
int l=calc(x,y),r=calc(a,b);
if(l*r==0) continue;
if(l>r) swap(l,r);
g[++tot]={l,i};
g[++tot]={r,-i};
//cout<<l<<" "<<r<<endl;
}
sort(g+1,g+tot+1);
rep(i,1,tot)
{
if(g[i].second<0)
{
if(tp&&st[tp]==-g[i].second) tp--;
else st[++tp]=g[i].second;
}
else
{
st[++tp]=g[i].second;
}
}
puts(tp>0?"N":"Y");
}
K(签到)
签到
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
const int Maxn=1000005;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n;
char ssr[Maxn];
int cnt[Maxn];
int tot;
int main() {
n=read();
for(int i=1;i<=n;++i) {
scanf("%s",ssr);
int len=strlen(ssr);
tot|=(1<<(ssr[0]-'A'));
for(int j=0;j<len;++j) {
cnt[i]|=(1<<(ssr[j]-'A'));
}
}
int flag=0;
for(int i=1;i<=n;++i) {
if((cnt[i]&tot)==cnt[i]) {
flag=1; break;
}
}
if(flag) cout<<"Y";
else cout<<"N";
}
L(期望)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll md = 1e9+7;
ll poww(ll a, ll b)
{
ll ans = 1;
for (; b; b>>=1, a=a*a%md)
if (b&1)
ans = ans*a%md;
return ans;
}
#define inv(x) (poww((x), md-2))
const int N = 2000000;
ll fac[N+5], ifac[N+5], pw2[N+5];
ll C(ll n, ll m)
{
return fac[n] * ifac[m] %md *ifac[n-m] %md;
}
ll calc(ll g, ll g1, ll g0, ll e, ll l)
{
ll ans = g;
ll t;
t = min(e, g1);
e -= t;
g1 -= t;
ans += t;
t = e / 2 * 2;
e -= t;
g0 -= t/2;
ans += t;
//e=0/1
if (e == 1)
{
--g0;
}
ll g1_l = 0;
t = min(g0, l);
l -= t;
g1_l += t;
if (e == 1 && l >= 1)
{
--l;
--e;
++ans;
}
t = min(g1, l);
l -= t;
g1_l -= l;
ans += g1_l;
return ans;
}
ll n, g, l, e;
int main()
{
int i;
fac[0] = 1;
for (i=1; i<=N; ++i)
{
fac[i] = fac[i-1] *i %md;
}
ifac[N] = inv(fac[N]);
for (i=N; i>=1; --i)
{
ifac[i-1] = ifac[i] *i %md;
}
pw2[0]=1;
for (i=1; i<=N; ++i)
{
pw2[i] = pw2[i-1] *2 %md;
}
//printf("%lld %lld %lld\n", ifac[0], ifac[1], ifac[2]);
scanf("%lld%lld%lld%lld", &n, &g, &l, &e);
if (2*n <= g)
{
g = 2*n;
l=e=0;
}
else if (2*n <= g+e)
{
e = 2*n-g;
l=0;
}
else if (2*n <= g+e+l)
{
l = 2*n-g-e;
}
ll ans = 0;
ll invall = inv(C(2*n, g));
for (i=0; i<=g/2; ++i)
{
if (n<i || n-i < g-2*i ) continue;
ll t1 = C(n, i)*C(n-i,g-2*i)%md*pw2[g-2*i]%md*invall%md;
ll t2 = calc(g, g-2*i, n-g+i, e, l);
ans = (ans + t1 * t2 ) %md;
//printf("--%d %lld %lld %lld %lld\n", i, g-2*i, n-g+i, t1, t2);
}
printf("%lld\n", ans);
return 0;
}
M(贪心)
\(ddl\)升序排序后check合法性,在合法情况下逐位判断能否交换使字典序最小
预处理信息使得可以\(O(1)\)怕判断能否交换
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Maxn=5005,Inf=0x3f3f3f3f;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n;
struct Task {
int t,d,i;
} task[Maxn];
inline bool cmp(const Task &A,const Task &B) {
return A.d<B.d;
}
ll sumt[Maxn];
int sub[Maxn],minsub[Maxn],pos[Maxn];
int used[Maxn],ans[Maxn];
int main() {
n=read();
for(int i=1;i<=n;++i) {
int t=read(),d=read();
task[i]=(Task){t,d,i};
}
sort(task+1,task+1+n,cmp);
minsub[0]=Inf;
for(int i=1;i<=n;++i) {
sumt[i]=sumt[i-1]+task[i].t;
if(sumt[i]>task[i].d) {
cout<<"*";
exit(0);
}
sub[i]=task[i].d-sumt[i];
minsub[i]=min(minsub[i-1],sub[i]);
pos[task[i].i]=i;
}
for(int i=1;i<=n;++i) {
for(int k=1;k<=n;++k) {
if(used[k]) continue;
int cur=pos[k];
if(minsub[cur-1]>=task[cur].t) {
Task temp=task[cur];
for(int t=cur;t>i;--t)
task[t]=task[t-1];
task[i]=temp;
used[k]=1;
break;
}
}
for(int k=1;k<=n;++k) {
sumt[k]=sumt[k-1]+task[k].t;
sub[k]=task[k].d-sumt[k];
if(k<=i) sub[k]=Inf;
minsub[k]=min(minsub[k-1],sub[k]);
pos[task[k].i]=k;
}
}
for(int i=1;i<=n;++i)
cout<<task[i].i<<" ";
return 0;
}