Hint:可以点击右下角的目录符号快速跳转到指定位置
上接:SSF信息社团寒假训练题目整理(二)
下接:SSF信息社团3月训练题目整理
Day 1(2.22)
1267K
容易得到一个结论:若不排序,则两个不同的数对应两个不同的序列,但任意一个序列不一定对一个一个数,因为这个序列有可能不合法。考虑怎样的一个序列 (a_1,a_2,a_3,cdots,a_n)(排序前)是合法的,不难看出当且仅当 (forall iin[1,n],a_ile i) 且 (a_n ot=0) 时,这个排列一定对应一个数 (x)。
求出给出的 (k_i) 对应的序列 (a)。由于最后一位不能是 (0),所以在 (a) 中枚举最后一位的值。对于前面 (n-1) 位,从大到小安排位置,假设当前还剩 (u) 个空位,正在安排的数有 (v) 个,那么合法方案有 (mathrm C_u^v=dfrac{u!}{v!(u-v)!}) 个,将所有的组合数乘起来就是枚举的最后一位所对应的合法序列的数量,最后将所有最后一位对应的合法序列的数量相加就是答案。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
int p[40];
int C(int n,int m) {return p[n]/p[n-m]/p[m];}
int n,b[40],a[40],cnt[40];
int calc()
{
// printf("b:
");
// for(int i=1;i<=n;i++) printf("%lld ",b[i]);
// putchar('
');
memset(cnt,0,sizeof(cnt));
for(int i=1;i<n;i++) cnt[b[i]]++;
int ans=1;
for(int i=n-1;i;i--)
{
if(b[i]==0) continue;
if(i==n-1 || b[i]!=b[i+1])
{
if(i-b[i]+1<cnt[b[i]]) return 0;
ans*=C(i-b[i]+1,cnt[b[i]]);
}
}
// printf("ans:%lld
",ans);
return ans;
}
void sol(int x)
{
n=0;
while(x)
{
n++;
a[n]=x%(n+1);
x/=n+1;
}
// for(int i=1;i<=n;i++) printf("%lld ",a[i]);
// for(int i=1;i<=n;i++) cnt[a[i]]++;
int ans=0;
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
if(a[i] && a[i]!=a[i-1])
{
b[n]=a[i];
for(int j=1,cc=0;j<=n;j++)
{
if(i==j) continue;
b[++cc]=a[j];
}
sort(b+1,b+n);
ans+=calc();
}
}
printf("%lld
",ans-1);
}
signed main()
{
p[0]=1;
for(int i=1;i<=20;i++) p[i]=p[i-1]*i;
// for(int i=1;i<=20;i++) printf("%lld ",p[i]);
int T;
scanf("%lld",&T);
while(T--)
{
int x;
scanf("%lld",&x);
sol(x);
}
return 0;
}
//3
//1
//11
//123456
1271D
如果想派兵驻守一个点,那么时间一定越靠后越好。对于所有的 (1le ile n),统计出所有能到达点 (i) 的编号最大的点 (v_i),并重新建图,将所有的 (v_i) 向 (i) 连一条边。
顺次攻占点时,若攻占不了就撤销前面驻守的点、减取获得的价值,直到能攻占为止,随后能驻守多少点就驻守多少点并拿走那个点的价值。具体实现时还有一些细节,比如说为了使答案最优,兵力有可能变成负数,详见代码。
彩蛋
文言版题解:若欲遣兵屯一点,则日必愈以后也。于诸法之 i,计出所能至也,之号最大者也 v_i,更建图,将所有之 v_i 向上连一边。
次取点时,若取胜则省前屯之点、减取得之也,至能拔而止,既能住几点则守几点而去其点之也。事成时尚有微细,如以中最,兵有可为负之数,详见代码。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
priority_queue<pair<int,int>,vector<pair<int,int> >,
greater<pair<int,int> > > que;
//first:value, second:vertex
inline void read(int &x)
{
x=0; int 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^48);
c=getchar();
}
x*=f;
}
const int N=5000;
int a[N+10],b[N+10],c[N+10];
int mx[N+10];
vector<pair<int,int> > e[N+10];
//first:value, second:vertex
int main()
{
int n,m,k;
read(n);read(m);read(k);
for(int i=1;i<=n;i++)
{
read(a[i]);read(b[i]);read(c[i]);
mx[i]=i;
}
for(int i=1;i<=m;i++)
{
int u,v;
read(u);read(v);
mx[v]=max(mx[v],u);
}
for(int i=1;i<=n;i++) e[mx[i]].push_back(make_pair(c[i],i));
// for(int i=1;i<=n;i++)
// {
// printf("i=%d:",i);
// for(int j=0;(unsigned)j<e[i].size();i++) printf(" %d",e[i][j].second);
// printf("
");
// }
for(int i=1;i<=n;i++) sort(e[mx[i]].begin(),e[mx[i]].end(),greater<pair<int,int> >());
int ans=0;
for(int i=1;i<=n;i++)
{
while(k<a[i] && !que.empty())
{
ans-=que.top().first;
que.pop();
k++;
}
if(k<a[i])
{
printf("-1");
return 0;
}
k+=b[i];
for(int j=0;(unsigned)j<e[i].size();j++)
{
k--;
ans+=e[i][j].first;
que.push(e[i][j]);
}
// printf("i=%d, k=%d, ans=%d
",i,k,ans);
}
// printf("%llu
",que.size());
while(!que.empty() && k<0)
{
ans-=que.top().first;que.pop();
k++;
}
printf("%d",ans);
return 0;
}
1266E
一个数对 ((s_j,t_j,u_j)) 带给答案的贡献必然是 (-1,0,+1) 之一,存在一个数对 ((s_i,t_i,u_i);(i<j)) 满足 (s_i=s_j,t_i=t_j) 时,((s_j,t_j,u_j)) 给答案 (-1) 的贡献,((s_i,t_i,u_i)) 给答案 (1) 的贡献,将贡献存到一个数组 (b_i) 中,如果对应的 (b_i<0),则对答案的对应的贡献为 (0)。求和使用 BIT(树状数组)即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
#define mp make_pair
const int N=2e5;
typedef long long ll;
map<pair<int,int>,int> p;
int a[N+10],b[N+10],n;
ll c[N+10];
void modify(int x,int d)
{
if(x==0) return;
for(;x<=n;x+=x&-x)
c[x]+=d;
}
ll query(int x)
{
ll ans=0;
for(;x;x-=x&-x)
ans+=c[x];
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
modify(i,a[i]);
}
memcpy(b,a,sizeof(a));
int m;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int s,t,u;
scanf("%d %d %d",&s,&t,&u);
int pre=p[mp(s,t)];
b[u]--;
if(b[u]>=0) modify(u,-1);
b[pre]++;
if(b[pre]>0 && pre) modify(pre,1);
p[mp(s,t)]=u;
printf("%lld
",query(n));
}
return 0;
}
Day 2(2.23)
1146D
打表可知,当 (ige a+b) 的时候,(f(i)) 所组成的序列非常有规律:
具体来说,就是每个数重复 (gcd(a,b)) 次,公差为 (1) 的等差数列。通过等差数列的通项公式,在求出 (gcd) 后可以 (mathcal O(1)) 算出 (sumlimits_{i=a+b}^mf(i))。对于 (i<a+b) 的情况,设 (t_i;(0le i<a+b)) 表示从 (0) 走到 (i) 需要经过的编号最大的点的最小值,可以通过类似最短路的方式求得。有了 (t_i),(f(i)) 就可以通过下面的代码片段求出:
for(int i=0;i<=min(a+b-1,m);i++) f[t[i]]++;
for(int i=1;i<=min(a+b-1,m);i++) f[i]+=f[i-1];
时间复杂度 (mathcal O(a+b))。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
inline void read(int &x)
{
x=0; int 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^48);
c=getchar();
}
x*=f;
}
const int N=2e5;
int f[N+10],t[N+10];
bool vis[N+10];
int a,b;
void dij()
{
priority_queue<pair<int,int> > que;
que.push(make_pair(0,0));
memset(t,0x3f,sizeof(t));
t[0]=0;
while(!que.empty())
{
int x=que.top().second;que.pop();
if(vis[x]) continue;
vis[x]=true;
int pre=x-b,nxt=x+a;
if(pre>=0 && max(t[x],pre)<t[pre])
{
t[pre]=max(t[x],pre);
que.push(make_pair(-t[pre],pre));
}
if(nxt<a+b && max(t[x],nxt)<t[nxt])
{
t[nxt]=max(t[x],nxt);
que.push(make_pair(-t[nxt],nxt));
}
}
}
typedef long long ll;
int gcd(int x,int y) {return !y?x:gcd(y,x%y);}
int main()
{
int m;
scanf("%d%d%d",&m,&a,&b);
dij();
for(int i=0;i<=min(m,a+b-1);i++)
if(t[i]<=N)
f[t[i]]++;
for(int i=1;i<=min(m,a+b-1);i++) f[i]+=f[i-1];
ll ans=0;
for(int i=0;i<=min(m,a+b-1);i++) ans+=f[i];
int g=gcd(a,b);
if(m<=a+b-1)
{
printf("%lld",ans);
return 0;
}
else
{
int sx=(a+b)/g+1,mx=m/g,xs=(mx-sx)+1;
ans+=(ll)(sx+mx)*xs*g/2;
int tmp=((m+1)%g==0?g:(m+1)%g);
ans+=(ll)tmp*(mx+1);
printf("%lld",ans);
}
return 0;
}
1151E
在一条非连通链中,若有 (V) 个点、(E) 条边,则通块数量为 (V-E)。将 (V) 和 (E) 分开计算。对于每一个 (a_i;(1le ile n)),(f(l,r)) 能够包含它当且仅当 (lle a_i) 且 (rge a_i),能够给答案带来 (a_i(n-a_i+1)) 的贡献;对于每一条边 ((a_i,a_{i+1});(1le i<n)),(f(l,r)) 能够包含它当且仅当 (llemin{a_i,a_{i+1}}) 且 (rgemax{a_i,a_{i+1}}),给答案带来 (-min{a_i,a_{i+1}}(n-max{a_i,a_{i+1}}+1)) 的贡献。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5;
#define int long long
int a[N+10],pre[N+10];
signed main()
{
int n;
int ans=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
ans+=a[i]*(n-a[i]+1);
}
for(int i=1;i<n;i++) ans-=min(a[i],a[i+1])*(n-max(a[i],a[i+1])+1);
printf("%lld",ans);
return 0;
}
1154F
将 (a_i) 从小到大排序后,取的一定是 (a_1sim a_k)。令 (f(i)) 表示从 (1) 到 (i) 都取完的最小花费,(i) 从 (0) 到 (k-1) 扫一遍,每次执行如下转移:
(f(k)) 即为所求。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
inline void read(int &x)
{
x=0;int 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^48);
c=getchar();
}
x*=f;
}
const int N=2e5;
int a[N+10],f[N+10];
int x[N+10],y[N+10];
int sum[N+10];
void ckmin(int &x,int y) {x=min(x,y);}
signed main()
{
int n,m,k;
scanf("%lld %lld %lld",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
sort(a+1,a+n+1);
// printf("a[i]:");
// for(int i=1;i<=n;i++) printf("%lld ",a[i]);
// puts("
=======") ;
for(int i=1;i<=k;i++) sum[i]=sum[i-1]+a[i];
for(int i=1;i<=m;i++) scanf("%lld%lld",&x[i],&y[i]);
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=0;i<k;i++)
{
ckmin(f[i+1],f[i]+a[i+1]);
for(int j=1;j<=m;j++)
{
if(i+x[j]>k) continue;
ckmin(f[i+x[j]],f[i]+sum[i+x[j]]-sum[i+y[j]]);
}
}
// puts("
====");
// for(int i=1;i<=k;i++) printf("%lld ",f[i]);
printf("%lld",f[k]);
return 0;
}
Day 3(2.24)
1202C
在某一个点前面插入一个字符的操作等同于将不插入字符时能走到的坐标集体向上/下/左/右挪一步,于是可以开四棵线段树,分别维护横坐标最小值、最大值,纵坐标最小值、最大值,枚举在哪个地方插入然后动态算答案即可。
要注意处理只有一个字符等特殊情况。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
const int N=2e5;
pair<int,int> pos[N+10];
char s[N+10];
struct Min
{
struct seg
{
int l,r,min,add;
seg(){add=0;}
}t[N*4+10];
void build(int p,int l,int r,int *a)
{
t[p].l=l;t[p].r=r;
if(l==r)
{
t[p].min=a[l];
return;
}
int mid=(l+r)/2;
build(p*2,l,mid,a);
build(p*2+1,mid+1,r,a);
t[p].min=min(t[p*2].min,t[p*2+1].min);
}
void spread(int p)
{
if(t[p].add)
{
t[p*2].add+=t[p].add;
t[p*2+1].add+=t[p].add;
t[p*2].min+=t[p].add;
t[p*2+1].min+=t[p].add;
t[p].add=0;
}
}
void modify(int p,int l,int r,int d)
{
if(l<=t[p].l && t[p].r<=r)
{
t[p].min+=d;
t[p].add+=d;
return;
}
spread(p);
int mid=(t[p].l+t[p].r)/2;
if(l<=mid) modify(p*2,l,r,d);
if(r>mid) modify(p*2+1,l,r,d);
t[p].min=min(t[p*2].min,t[p*2+1].min);
}
int query(int p,int l,int r)
{
if(l<=t[p].l && t[p].r<=r) return t[p].min;
spread(p);
int mid=(t[p].l+t[p].r)/2;
int ans=0x7fffffff;
if(l<=mid) ans=min(ans,query(p*2,l,r));
if(r>mid) ans=min(ans,query(p*2+1,l,r));
return ans;
}
}u,l;
struct Max
{
struct seg
{
int l,r,max,add;
seg(){add=0;}
}t[N*4+10];
void build(int p,int l,int r,int *a)
{
t[p].l=l;t[p].r=r;
if(l==r)
{
t[p].max=a[l];
return;
}
int mid=(l+r)/2;
build(p*2,l,mid,a);
build(p*2+1,mid+1,r,a);
t[p].max=max(t[p*2].max,t[p*2+1].max);
}
void spread(int p)
{
if(t[p].add)
{
t[p*2].add+=t[p].add;
t[p*2+1].add+=t[p].add;
t[p*2].max+=t[p].add;
t[p*2+1].max+=t[p].add;
t[p].add=0;
}
}
void modify(int p,int l,int r,int d)
{
if(l<=t[p].l && t[p].r<=r)
{
t[p].max+=d;
t[p].add+=d;
return;
}
spread(p);
int mid=(t[p].l+t[p].r)/2;
if(l<=mid) modify(p*2,l,r,d);
if(r>mid) modify(p*2+1,l,r,d);
t[p].max=max(t[p*2].max,t[p*2+1].max);
}
int query(int p,int l,int r)
{
if(l<=t[p].l && t[p].r<=r) return t[p].max;
spread(p);
int mid=(t[p].l+t[p].r)/2;
int ans=-0x7fffffff;
if(l<=mid) ans=max(ans,query(p*2,l,r));
if(r>mid) ans=max(ans,query(p*2+1,l,r));
return ans;
}
}d,r;
#define mp make_pair
const int nxt[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
// u, d, l, r
// W, S, A, D
int x[N+10],y[N+10];
#define f(x) (x.query(1,0,n))
void sol()
{
// memset(pos,0,sizeof(pos));
pos[0]=mp(0,0);
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;i++)
{
int p;
if(s[i]=='W') p=0;
else if(s[i]=='S') p=1;
else if(s[i]=='A') p=2;
else p=3;
pos[i]=make_pair(pos[i-1].first+nxt[p][0],pos[i-1].second+nxt[p][1]);
}
for(int i=0;i<=n;i++)
{
x[i]=pos[i].first;
y[i]=pos[i].second;
}
u.build(1,0,n,x);
d.build(1,0,n,x);
l.build(1,0,n,y);
r.build(1,0,n,y);
int ans=(f(d)-f(u)+1)*(f(r)-f(l)+1);
// printf("ans:%lld
",ans);
for(int i=1;i<=n;i++) //在i的前面插
{
// Max dd=d,rr=r;
// Min uu=u,ll=l;
// W
u.modify(1,i,n,-1);
d.modify(1,i,n,-1);
int xx=x[i-1]+nxt[0][0],yy=y[i-1]+nxt[0][1];
// printf("(%lld, %lld) -> (%lld, %lld)
",f(u),f(l),f(d),f(r));
// printf("xx:%lld, yy:%lld
",xx,yy);
ans=min(ans,(max(f(d),xx)-min(f(u),xx)+1)
*(max(f(r),yy)-min(f(l),yy)+1));
u.modify(1,i,n,1);
d.modify(1,i,n,1);
// printf("%lldW:%lld
",i,ans);
// S
u.modify(1,i,n,1);
d.modify(1,i,n,1);
xx=x[i-1]+nxt[1][0],yy=y[i-1]+nxt[1][1];
ans=min(ans,(max(f(d),xx)-min(f(u),xx)+1)
*(max(f(r),yy)-min(f(l),yy)+1));
u.modify(1,i,n,-1);
d.modify(1,i,n,-1);
// printf("%lldS:%lld
",i,ans);
// printf("(%lld, %lld) -> (%lld, %lld)
",f(u),f(l),f(d),f(r));
// A
l.modify(1,i,n,-1);
r.modify(1,i,n,-1);
xx=x[i-1]+nxt[2][0],yy=y[i-1]+nxt[2][1];
ans=min(ans,(max(f(d),xx)-min(f(u),xx)+1)
*(max(f(r),yy)-min(f(l),yy)+1));
l.modify(1,i,n,1);
r.modify(1,i,n,1);
// printf("%lldA:%lld
",i,ans);
// printf("(%lld, %lld) -> (%lld, %lld)
",f(u),f(l),f(d),f(r));
// D
l.modify(1,i,n,1);
r.modify(1,i,n,1);
xx=x[i-1]+nxt[3][0],yy=y[i-1]+nxt[3][1];
ans=min(ans,(max(f(d),xx)-min(f(u),xx)+1)
*(max(f(r),yy)-min(f(l),yy)+1));
l.modify(1,i,n,-1);
r.modify(1,i,n,-1);
// printf("%lldD:%lld
",i,ans);
// printf("(%lld, %lld) -> (%lld, %lld)
",f(u),f(l),f(d),f(r));
}
printf("%lld
",ans);
}
signed main()
{
int T;
scanf("%lld",&T);
while(T--)
sol();
return 0;
}
1204D2
题意其实就是问最多能把多少个 (1) 变成 (0)。可以看出,若 (a_i=1),它能变成 (0) 的情况当且仅当后面 (1) 的数量大于等于 (0) 的数量,统计即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5;
char s[N+10];
int main()
{
scanf("%s",s+1);
int n=strlen(s+1);
int cnt1=0,cnt0=0;
for(int i=n;i;i--)
{
if(s[i]=='0') cnt0++;
else
{
if(cnt1>=cnt0)
{
cnt0++;
s[i]='0';
}
cnt1++;
}
}
printf("%s",s+1);
return 0;
}
1203F1
将 (b_i<0) 和 (b_ige 0) 的情况分开。对于 (b_ige 0) 的情况,按照 (a_i) 从小到大排序;对于 (b_i<0) 的情况,按照 (a_i+b_i) 从大到小排序。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100;
struct node
{
int a,b;
node(int aa,int bb) {a=aa;b=bb;}
node(){}
}a[N+10],b[N+10];
bool cmp1(node x,node y) {return x.a!=y.a?x.a<y.a:x.b>y.b;}
bool cmp2(node x,node y) {return x.a+x.b>y.a+y.b;}
int c1=0,c2=0;
int n,r;
bool check()
{
for(int i=1;i<=c1;i++)
{
if(r<a[i].a)
return 0;
else r+=a[i].b;
}
for(int i=1;i<=c2;i++)
{
if(r<b[i].a)
return 0;
else r+=b[i].b;
if(r<0)
return 0;
}
return 1;
}
int main()
{
scanf("%d %d",&n,&r);
int rr=r;
for(int i=1;i<=n;i++)
{
int aa,bb;
scanf("%d %d",&aa,&bb);
if(bb>=0) a[++c1]=node(aa,bb);
else b[++c2]=node(aa,bb);
}
sort(a+1,a+c1+1,cmp1);
sort(b+1,b+c2+1,cmp2);
if(check())
{
puts("YES");
return 0;
}
else puts("NO");
return 0;
}
Day 3(2.25)
1364D
I have a proof that for any input you can always solve at least one of these problems, but it's left as an exercise for the reader.
所以就来证明一下吧。
如果图是一棵树,就将它的点染一下色,使得对于任意的边 (u-v),(u) 和 (v) 的颜色不同,那么同种颜色的点组成的集合就是一个独立集。选择数量较多的那个,假设它的数量为 (r),若 (r>k) 则在独立集里面随便扔 (r-k) 个点,最终剩下的点组成的独立集就是情况 (1) 的构造方案。
如果不是一棵树,那么在图里随便找一个环,假设它的长度是 (l),如果 (lle k),就是情况 (2);如果 (l>k),那么隔一个取一个点,就构成了一个大小为 (lceil l/2 ceil) 个独立集,这个数 (ge lceil k/2 ceil),随便扔掉 (lceil l/2 ceil-lceil k/2 ceil) 个点就是情况 (1)。但如果使用上面的情况构造情况 (1) 实际上在环上不相邻的两个点有连边的情况下是不对的,如图:
假设找到的环是 (1-2-3-4-5-1),(1) 和 (4) 有连边,那么选择 (1) 和 (4) 是不对的。这个时候可以把节点 (5) 去掉,这个环就变成了 (1-2-3-4-1),如果这个环中还有连边(当然,这张图中没有)就再次执行刚才的操作直到环变成上面图中 (1-2-3-4-1) 的形态位置,然后对再判断这个环的大小 (l) 是否满足 (l> k) 或 (lle k) 并进行上面讨论的独立集或环的构造就是对的。
时间复杂度为 (mathcal O(n+m))。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
inline void read(int &x)
{
x=0;int 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^48);c=getchar();}
x*=f;
}
const int N=1e5,M=4e5;
int head[N+10],ver[M+10],nxt[M+10],tot=0;
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
int st[N+10],top=0;
bool vis[N+10];
bool dfs(int x,int fa)
{
st[++top]=x;
if(vis[x]) return true;
vis[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(y==fa) continue;
if(dfs(y,x)) return true;
}
top--;
vis[x]=0;
return false;
}
/*tree*/
int col[N+10];
void dfs1(int x,int fa,int c)
{
col[x]=c;
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(y==fa) continue;
dfs1(y,x,c^1);
}
}
/**/
/*邻接表*/
int pre[N+10];
/**/
int x[M+10],y[M+10];
int main()
{
int n,m,k;
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
int xx,yy;
scanf("%d %d",&xx,&yy);
add(xx,yy);
add(yy,xx);
x[i]=xx;
y[i]=yy;
}
if(m==n-1)
{
dfs1(1,-1,1);
k=(k+1)/2;
int cnt=0;
for(int i=1;i<=n;i++) cnt+=(col[i]==1);
if(cnt>=n-cnt)
{
printf("1
");
for(int i=1;i<=n && k;i++)
{
if(col[i]==1)
{
k--;
printf("%d ",i);
}
}
}
else
{
printf("1
");
for(int i=1;i<=n && k;i++)
{
if(col[i]==0)
{
k--;
printf("%d ",i);
}
}
}
return 0;
}
// puts("HELLO!");
dfs(1,0);
memset(vis,0,sizeof(vis));
// puts("Stack:");
// for(int i=1;i<=top;i++) printf("%d ",st[i]);
// puts("");
int zzt=st[top--],l=zzt;
vis[zzt]=1;
while(st[top]!=zzt)
{
int tmp=st[top--];
pre[tmp]=l;
l=tmp;
vis[tmp]=1;
}
// puts("pre:");
// for(int i=1;i<=n;i++)
// printf("%d ",pre[i]);
pre[zzt]=l;
for(int i=1;i<=m;i++)
{
int u=x[i],v=y[i];
if(pre[u]!=v && pre[v]!=u && vis[u] && vis[v])
{
for(int j=pre[u];j!=v;j=pre[j])
vis[j]=0;
pre[u]=v;
}
}
int cnt=0;
for(int i=1;i<=n;i++)
{
cnt+=vis[i];
if(vis[i])
zzt=i;
}
if(cnt<=k)
{
printf("2
%d
",cnt);
printf("%d ",zzt);
for(int i=pre[zzt];i!=zzt&&k;i=pre[i])
printf("%d ",i);
}
else
{
printf("1
");
k=(k+1)/2;
k--;
printf("%d ",zzt);
for(int i=pre[pre[zzt]];i!=zzt&&k;i=pre[pre[i]])
{
printf("%d ",i);
k--;
}
}
return 0;
}
1366E
由于 (b_i) 单调递增,所以可以双指针将 (a) 和 (b) 同时扫一遍,执行如下操作:
(
igets m\
jgets n\
ansgets 1\
mathbf{while};i>1:\
quad cntgets 0\
quad pre_jgets j\
quad mathbf{while};a_jge b_i:\
quadquad cntgets cnt+left[exists kin[j,pre_j],a_j=b_i
ight]\
quadquad jgets j-1\
quad mathbf{end;while}\
quad igets i-1\
quad ansgets ans imes cnt\
mathbf{end;while}\
mathbf{print}; ans
)
(j) 的作用是区间的末尾,(b_i) 就是这个区间的最小值。简单来说就是每个 (b_i) 在当前对应的 (a_j) 前面算出有多少个连续的 (a_jge b_i),因为这个区间必须包含 (b_i),所以说 (cnt) 累加的时候加的是一个 (left[exists kin[j,pre_j],a_j=b_i ight]),按照乘法原理将 (cnt) 相乘即为答案。
需要特判 (b) 里面的数是否 (a) 中都有,如果不是都有答案就是 (0)。还需要判断 (min{a_i}<b_1) 是否成立,若不成立同样输出 (0)。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=4e5;
typedef long long ll;
int a[N+10],b[N+10],c[N+10];
bool vis[N+10];
int main()
{
int n,m;
scanf("%d %d",&n,&m);
int cc=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
c[++cc]=a[i];
}
for(int i=1;i<=m;i++)
{
scanf("%d",&b[i]);
c[++cc]=b[i];
}
sort(c+1,c+cc+1);
cc=unique(c+1,c+cc+1)-c-1;
for(int i=1;i<=n;i++) vis[a[i]=lower_bound(c+1,c+cc+1,a[i])-c]=1;
for(int i=1;i<=m;i++)
{
b[i]=lower_bound(c+1,c+cc+1,b[i])-c;
if(!vis[b[i]])
{
puts("0");
return 0;
}
}
ll ans=1;
int i,j;
for(i=m,j=n;i&&j;i--)
{
if(i==1) continue;
int cnt=0;
bool flag=0;
while(a[j]>=b[i])
{
if(a[j]==b[i]) flag=1;
cnt+=flag;
j--;
}
ans*=cnt;
ans%=998244353;
}
for(int i=1;i<=n;i++)
if(a[i]<b[1])
{
ans=0;
break;
}
// if(j) ans=0;
printf("%lld",ans);
return 0;
}
1365F
引理:两个数在交换前关于中心对称,那么交换后也对称。例如 (1,2,3,4,5,6) 将前两个和后两个交换后变成 (5,6,3,4,1,2),(1) 和 (6) 在交换前后都对称。证明显然。
然后在 (b) 数组中一个一个验证,看是无序数对 ((b_i,b_{n-i+1})) 和 ((a_i,a_{n-i+1})) 能否一一对应即可。(n,tle 200),所以 (n^2) 暴力枚举即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=500;
int a[N+10],b[N+10],n;
bool vis[N+10];
bool sol()
{
memset(vis,0,sizeof(vis));
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
if(n%2 && a[n/2+1]!=b[n/2+1])
return false;
for(int i=1;i<=n/2;i++)
{
bool flag=0;
for(int j=1;j<=n;j++)
{
if(n%2 && j==n/2+1) continue;
if(b[i]==a[j] && b[n-i+1]==a[n-j+1] && !vis[j] && !vis[n-j+1])
{
vis[j]=vis[n-j+1]=1;
flag=1;
break;
}
}
// printf("i=%d, flag=%d
",i,flag);
// for(int j=1;j<=n;j++) printf("%d",vis[j]);
// putchar('
');
if(!flag) return false;
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while(T--) puts(sol()?"YES":"NO");
return 0;
}