文章目录
综合训练赛一
1. 捣蛋的ly(字符串+贪心)
Solution
:
采用贪心策略来做题,找到峰值,即递减序列的第一个:
- 输入字符串与要删的数
- 处理前,特判特殊情况
- 找递减序列的第一个,如果找到了,则将这个位置标记,并且将位置 w w w赋值为 j j j,退出内层循环
- 将位置w以后的数组元素全部向前移动一位
- 输出,并且特判前导0
完整程序
:
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
typedef long long ll;
string st;
ll s,p,w,len;
int main()
{
cin>>st>>s;
len=st.size();
/* 特判特殊情况 */
if(st[0]=='1'&&st[1]=='0'&&s==1)
{
cout<<'0';
return 0;
}
for(int i=1;i<=s;i++)
{
for(int j=0;j<=len-i+1;j++)
{
if(st[j]>st[j+1]||j==len-i+1)
{ /* 记录下递减序列的第一个的位置,并打上标记 */
w=j;
st[j]='*';
break;
}
}
/* 将标记元素之后的元素向前移动 */
for(int j=w+1;j<len;j++) st[j-1]=st[j],st[j]='*';
}
for(int i=0;i<len-s;i++)
{
if(st[i]!='0') p=1,cout<<st[i];
/* 如果是前导0,则不输出 */
if(st[i]=='0'&&p==1) cout<<st[i];
}
return 0;
}
2. 圣诞节(动态规划+树形dp)
Solution
:
经典的树形DP,进入正题:
-
f a [ i ] [ j ] fa[i][j] fa[i][j]表示 i i i这个点上色为 j j j是方案数(从下往上/从子节点向父节点更新)
-
即对于所有初始节点, f a [ i ] [ 1 ] , f a [ i ] [ 2 ] , f a [ i ] [ 3 ] fa[i][1],fa[i][2],fa[i][3] fa[i][1],fa[i][2],fa[i][3]都为1
-
当某个节点被指定上色后,那么该节点另外两种颜色的方案数为0.
-
例如:当点 x x x被指定上色2时, f [ x ] [ 0 ] = 0 , f [ x ] [ 3 ] = 0 f[x][0]=0,f[x][3]=0 f[x][0]=0,f[x][3]=0(因为无法上色1和3)
-
对于每个节点,因为不能于子节点上色相同,即:
f a [ s o n ] [ i ] = f a [ s o n ] [ i ] ∗ ( ( f a [ e [ i ] . v ] [ j ] + f a [ e [ i ] . v ] [ k ] ) % m o d ) % m o d ( i = 0 , j = 1 , k = 2 ; i = 1 , j = 0 , k = 2 ; i = 2 , j = 0 , k = 1 ) fa[son][i]=fa[son][i]*((fa[e[i].v][j]+fa[e[i].v][k])\%mod)\%mod(i=0,j=1,k=2;i=1,j=0,k=2;i=2,j=0,k=1) fa[son][i]=fa[son][i]∗((fa[e[i].v][j]+fa[e[i].v][k])%mod)%mod(i=0,j=1,k=2;i=1,j=0,k=2;i=2,j=0,k=1) -
数据范围方面:取模和开long long
完整程序
:
#include <iostream>
using namespace std;
#define ll long long
const int mod = 1e9 + 7;
const int MAX_N = 2e5 + 100;
int n,k,x,y,head[MAX_N],tot;
ll fa[MAX_N][3];
struct node{
int v,next;
}e[MAX_N<<1];
/* 速读 */
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-f;
ch=getchar();
}
while(ch<='9'&&ch>='0') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
/* 建边 */
inline void add_edge(int u,int v){
e[++tot].next=head[u];
head[u]=tot;
e[tot].v=v;
}
inline void dfs(int son,int father){
/* 当某个节点被指定上色后,那么该节点另外两种颜色的方案数为0 */
for(int i=0;i<3;i++){
if(fa[son][i]){
for(int j=0;j<i;j++)
fa[son][j]=0;
break;
}
fa[son][i]=1;
}
/* 链式前向星遍历 */
for(int i=head[son];i;i=e[i].next){
if(e[i].v!=father){
dfs(e[i].v,son);
fa[son][0]=fa[son][0]*((fa[e[i].v][1]+fa[e[i].v][2])%mod)%mod;
fa[son][1]=fa[son][1]*((fa[e[i].v][0]+fa[e[i].v][2])%mod)%mod;
fa[son][2]=fa[son][2]*((fa[e[i].v][0]+fa[e[i].v][1])%mod)%mod;
}
}
}
int main(){
n=read();k=read();
for(int i=1;i<n;i++) x=read(),y=read(),add_edge(x,y),add_edge(y,x);
for(int i=1;i<=k;i++) x=read(),y=read()-1,fa[x][y]=1;
dfs(1,0);
printf("%lld",(fa[1][0]+fa[1][1]+fa[1][2])%mod);
return 0;
}
3. 种田(动态规划)
Solution
:
-
以 ( i , j ) (i,j) (i,j)为右下角结点,当 a [ i ] [ j ] = = 1 a[i][j]==1 a[i][j]==1时,检查左、上、左上角的结点是否为0,以 f [ i ] [ j ] f[i][j] f[i][j]来表示以 ( i , j ) (i,j) (i,j)为结点的最大正方形的边长。
-
先对数据进行预处理,将数组元素手动赋值为1
-
标记不能通过的点的坐标对应的元素为0
完整程序
:
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1005;
int a[N][N];
int dp[N][N];
int n,m,p,q;
int ans=0;
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=1;
}
}
for(int i=1;i<=m;i++){
scanf("%d %d",&p,&q);
a[p][q]=0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(a[i][j]==1){
dp[i][j]=min(min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;
}
ans=max(ans,dp[i][j]);
}
}
printf("%d",ans);
return 0;
}
4. 解密童话2
Solution
:
- 定义一个sum数组来表示该节点有多少个字符串经过
完整程序
:
#include <iostream>
#include <cstring>
using namespace std;
int n,m,k,ch[500001][3],tot=1,bo[500001],sum[500001],x;
bool p[500001];
inline int read(){
int data=0,w=1; char ch=0;
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
return data*w;
}
void add(bool p[]) {
int u=1;
for(int i=1; i<=k; i++) {
int c=p[i];
if(ch[u][c]==-1) ch[u][c]=++tot;
u=ch[u][c];
sum[u]++;
}
bo[u]++;
}
int find(bool p[]) {
int u=1;
int res=0;
for(int i=1; i<=k; i++) {
int c=p[i];
if(ch[u][c]==-1) return res;
u=ch[u][c];
res+=bo[u];
}
return res-bo[u]+sum[u];
}
int main() {
int x;
m=read();
n=read();
memset(ch,-1,sizeof ch);
for(int i=1; i<=m; i++) {
k=read();
for(int j=1; j<=k; j++) {
p[j]=read();
}
add(p);
}
for(int i=1; i<=n; i++) {
k=read();
for(int j=1; j<=k; j++) {
p[j]=read();
}
printf("%d\n",find(p));
}
}
5. 落魄的wyh比赛版
Solution
:
- 考虑到 a [ i ] a[i] a[i]的取值范围,数组空间应该申请为1e6
- 离线算法之莫队算法普通模板,对if-else中的判断稍作改进
完整程序
:
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 1000100;
int n,q;
int a[N],vis[N],s[N],now,ans[N];
int l,r;
struct node{
int l,r,id;
bool operator <(const node b) const{
return s[l]==s[b.l]?r<b.r:s[l]<s[b.l];
}
}Q[N];
/* 速读 */
inline int read(){
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-f;ch=getchar();}
while (ch<='9'&&ch>='0') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
inline void del(int x){vis[a[x]]--;if (!vis[a[x]]) now--;}
inline void add(int x){vis[a[x]]++;if (vis[a[x]]==1) now++;}
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
q=read();
for(int i=1;i<=q;i++){
Q[i].l=read();Q[i].r=read();Q[i].id=i;
}
int si=sqrt(n);
int num=ceil((double)n/si);
for(int i=1;i<=num;i++)
for(int j=(i-1)*si+1;j<=i*si;j++)
s[j]=i;
sort(Q+1,Q+q+1);
for(int i=1;i<=q;i++){
while(l<Q[i].l) del(l++);
while(l>Q[i].l) add(--l);
while(r<Q[i].r) add(++r);
while(r>Q[i].r) del(r--);
if(now==Q[i].r-Q[i].l+1) ans[Q[i].id]=1;
else ans[Q[i].id]=now;
}
for(int i=1;i<=q;i++){
if(ans[i]==1) printf("%d\n",Q[i].r-Q[i].l+1);
else printf("%d\n",ans[i]);
}
return 0;
}
6. 落魄的wyh数据增强版
Solution
:
- 按r排序一下
- 树状数组tree[j]维护从1到j不同数字的个数有多少个
- 然后用前缀和的思想(tree[r]-tree[l-1])
完整程序
:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAX_N 1000100
int ans[MAX_N],sta[MAX_N],tree[MAX_N],n,q,B[MAX_N];
struct node{
int l,r;
int pos;
bool operator <(const node &b)const{
return r<b.r;
}
}query[MAX_N];
int lowbit(int i){
return i&(-i);
}
void add(int i,int now){
while(i<=n){
tree[i]+=now;
i+=lowbit(i);
}
}
int sum(int i){
int ans=0;
while(i!=0){
ans+=tree[i];
i-=lowbit(i);
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&sta[i]);
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%d %d",&query[i].l,&query[i].r);
query[i].pos=i;
}
sort(query+1,query+1+q);
int nex=1;
for(int i=1;i<=q;i++){
for(int j=nex;j<=query[i].r;j++){
if(B[sta[j]]) add(B[sta[j]],-1);
add(j,1);
B[sta[j]]=j;
}
nex=query[i].r+1;
ans[query[i].pos]=sum(query[i].r)-sum(query[i].l-1);
}
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}
7. 魔法少女(异或和)
Solution
:
- a x o r b < = a + b a\ xor \ b <=a+b a xor b<=a+b,即分组越少,值越小
完整程序
:
#include<iostream>
using namespace std;
int main(){
int n,a,ans;
scanf("%d",&n);
scanf("%d",&ans);
for(int i=2;i<=n;i++){
scanf("%d",&a);
ans^=a;
}
cout<<ans;
return 0;
}
8. bbh surprises wyh
Solution
:
可以操作的基元只有 ( a , b ) , ( a , − b ) , ( b , a ) , ( b , − a ) (a,b),(a,-b),(b,a),(b,-a) (a,b),(a,−b),(b,a),(b,−a),因为加上 ( − a , − b ) (-a,-b) (−a,−b)相当于减去 ( a , b ) (a,b) (a,b),其他的同理。
那么问题转化为了找到整数
c
,
d
,
e
,
f
c,d,e,f
c,d,e,f,使得:
c
(
a
,
b
)
+
d
(
a
,
−
b
)
+
e
(
b
,
a
)
+
f
(
b
,
−
a
)
=
(
x
,
y
)
c(a,b)+d(a,-b)+e(b,a)+f(b,-a)=(x,y)
c(a,b)+d(a,−b)+e(b,a)+f(b,−a)=(x,y)
化简上式有:
(
c
+
d
)
a
+
(
e
+
f
)
b
=
x
(c+d)a+(e+f)b=x
(c+d)a+(e+f)b=x
( e − f ) a + ( c − d ) b = y (e-f)a+(c-d)b=y (e−f)a+(c−d)b=y
根据裴蜀定理,方程 a x + b y = c ax+by=c ax+by=c有整数解 ( a , b ) (a,b) (a,b),当且仅当 g c d ( x , y ) ∣ c gcd(x,y)|c gcd(x,y)∣c成立
记 P ( x , y , c ) P(x,y,c) P(x,y,c)为真表示上式成立,那么问题进一步转化为 P ( a , b , x ) & & P ( a , b , y ) P(a,b,x)\&\&P(a,b,y) P(a,b,x)&&P(a,b,y)为真,且保证 c , d , e , f c,d,e,f c,d,e,f为整数。
要讨论 c , d , e , f c,d,e,f c,d,e,f的奇偶性,先讨论 c + d c+d c+d, c − d c-d c−d, e + f e+f e+f, e − f e-f e−f的奇偶性。
-
当 ( c + d ) = ( e + f ) = ( e − f ) = ( c − d ) = 0 ( m o d 2 ) (c+d)=(e+f)=(e-f)=(c-d)=0(mod\ 2) (c+d)=(e+f)=(e−f)=(c−d)=0(mod 2)时,我们发现 c + d 2 , e + f 2 , e − f 2 , c − d 2 \frac{c+d}{2},\frac{e+f}{2},\frac{e-f}{2},\frac{c-d}{2} 2c+d,2e+f,2e−f,2c−d的取值范围为整数。
即有:
c + d 2 a + e + f 2 b = x 2 , e − f 2 a + c − d 2 b = y 2 \frac{c+d}{2}a+\frac{e+f}{2}b=\frac{x}{2},\frac{e-f}{2}a+\frac{c-d}{2}b=\frac{y}{2} 2c+da+2e+fb=2x,2e−fa+2c−db=2y
根据裴蜀定理有, P ( a , b , x 2 ) & & P ( a , b , y 2 ) P(a,b,\frac{x}{2})\&\&P(a,b,\frac{y}{2}) P(a,b,2x)&&P(a,b,2y),即 P ( 2 a , 2 b , x ) & & P ( 2 a , 2 b , y ) P(2a,2b,x)\&\&P(2a,2b,y) P(2a,2b,x)&&P(2a,2b,y)为真。 -
当 c + d c+d c+d, e + f e+f e+f, e − f e-f e−f, c − d c-d c−d中有某一组或者两组都是奇数,以 c + d c+d c+d, c − d c-d c−d为例:
由
( c + d ) a + ( e + f ) b = x , ( e − f ) a + ( c − d ) b = y (c+d)a+(e+f)b=x,(e-f)a+(c-d)b=y (c+d)a+(e+f)b=x,(e−f)a+(c−d)b=y
得:
( c + d + 1 ) a + ( e + f ) b = x + a , ( e − f ) a + ( c − d + 1 ) b = y + b (c+d+1)a+(e+f)b=x+a,(e-f)a+(c-d+1)b=y+b (c+d+1)a+(e+f)b=x+a,(e−f)a+(c−d+1)b=y+b
回到偶数的情况 P ( 2 a , 2 b , x + a ) & & P ( 2 a , 2 b , y + b ) P(2a,2b,x+a)\&\&P(2a,2b,y+b) P(2a,2b,x+a)&&P(2a,2b,y+b)为真。 -
综上所述,当:
P ( 2 a , 2 b , x ) & & P ( 2 a , 2 b , y ) P(2a,2b,x)\&\&P(2a,2b,y) P(2a,2b,x)&&P(2a,2b,y)为真。
P ( 2 a , 2 b , x + a ) & & P ( 2 a , 2 b , y + b ) P(2a,2b,x+a)\&\&P(2a,2b,y+b) P(2a,2b,x+a)&&P(2a,2b,y+b)为真。
P ( 2 a , 2 b , x + b ) & & P ( 2 a , 2 b , y + a ) P(2a,2b,x+b)\&\&P(2a,2b,y+a) P(2a,2b,x+b)&&P(2a,2b,y+a)为真。
P ( 2 a , 2 b , x + a + b ) & & P ( 2 a , 2 b , y + a + b ) P(2a,2b,x+a+b)\&\&P(2a,2b,y+a+b) P(2a,2b,x+a+b)&&P(2a,2b,y+a+b)为真。
即方程 h ( a , b , x , y ) h(a,b,x,y) h(a,b,x,y)为真。
完整程序
:
#include <iostream>
using namespace std;
typedef long long ll;
ll gcd(ll x,ll y){
return y ? gcd(y,x%y) : x;
}
bool check(ll x,ll y,ll z){
return !(z%gcd(x,y));
}
int t;
ll a,b,x,y;
int main(){
scanf("%d",&t);
while(t--){
scanf("%lld %lld %lld %lld",&a,&b,&x,&y);
int flag=(check(2*a,2*b,x)&&check(2*a,2*b,y))||(check(2*a,2*b,x+a)&&check(2*a,2*b,y+b))||(check(2*a,2*b,x+b)&&check(2*a,2*b,y+a))||(check(2*a,2*b,x+a+b)&&check(2*a,2*b,y+a+b));
if(flag)
printf("Y\n");
else printf("N\n");
}
return 0;
}
9. 地下通道(Kruskal)
Solution
:
基于并查集的卡鲁斯卡尔算法
完整程序
:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2005;
struct node{
int u,v,dis;
bool operator <(const node&b)const{
return dis<b.dis;
}
}e[N*1000];
/* 数组要开大点 */
int n,c,tot;
int px[N],py[N];
int fa[N];
inline void add_edge(int u,int v,int w){
e[++tot].u=u;
e[tot].v=v;
e[tot].dis=w;
}
inline int add(int x){
if(fa[x]!=x) fa[x]=add(fa[x]);
return fa[x];
}
inline void kruskal(){
int u,v,dis;
int cnt=0,ans=0;
for(int i=1;i<=n;i++){
fa[i]=i;
}
for(int i=1;i<=tot;i++){
u=e[i].u,v=e[i].v;
u=add(u),v=add(v);
if(u!=v){
fa[u]=v;
ans+=e[i].dis;
cnt++;
}
if(cnt==n-1) break;
}
if(cnt==n-1) printf("%d",ans);
else puts("-1");
}
inline int cal(int i,int j){
return (px[i]-px[j])*(px[i]-px[j])+(py[i]-py[j])*(py[i]-py[j]);
}
int main(){
scanf("%d %d",&n,&c);
/* 预处理 */
for(int i=1;i<=n;i++){
scanf("%d %d",&px[i],&py[i]);
for(int j=1;j<i;j++){
int w=cal(i,j);
if(w>=c){
add_edge(i,j,w);
}
}
}
sort(e+1,e+1+tot);
kruskal();
return 0;
}
10. 还有少女们
Solution
:
直接使用邻接矩阵存储。并且记录每个点入度。
然后依次删掉入度为0的点,每删一个更新与这个点相邻的点的入度。然后再删掉入度为0的点,一直重复,直到没有入度为0的点或删完为止。
完整程序
:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 505;
int n,m,x,k,graph[N][N],in[N],q[10*N],l=1,r,maxx;
bool vis[N]; /* 记录有无这个点 */
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d %d",&x,&m);
vis[x]=true;
for(int j=1;j<=m;j++){
scanf("%d",&k);
graph[x][k]=1;
in[k]++;
}
maxx=max(maxx,x); /* 最大点的编号 */
}
/* 拓扑排序 */
for(int i=0;i<=maxx;i++)
if(in[i]==0&&vis[i]) q[++r]=i; /* 将入度为0的点放入队列 */
int ans=0;
while(ans<n){
if(l>r){ /* 不可再删 */
printf("%d",n-ans);
return 0;
}
int cmd=q[l++]; /* 删点 */
for(int i=0;i<=maxx;i++){
if(graph[cmd][i]&&vis[i]){
in[i]--;
if(in[i]==0&&vis[i]) q[++r]=i;
}
}
ans++; /* 更新已删点的个数 */
}
if(ans==n) printf("YES");
return 0;
}
11. 生蚝算账
参考题解
:
12. 好学的xym
参考题解
:
13. 伊雷娜与《妮可的冒险谭》
Solution
:
- 读入数据先按照花色、数字进行排序和去重。
- 枚举每一个点作为同花顺的右端点。
- 找到左侧可以连成同花顺的第一张牌,计算同花顺长度,更新最长的长度。
- 计算剩余的牌的数量,输出即可。
先排序,使相同花色的在一起,并且满足相同花色的数字从小到大排列,然后可以用queue
维护,遇到新的不同的花色就清空队列,否则就删除队首不满足的,然后将当前这个枚举到的结尾插入队列,然后每次取个最大的size
,最后求得最大的不用更换的牌数(然后用总排数减去就是最终答案了)
完整程序
:
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int n,ans,cnt;
struct node{
int l,r;
bool operator<(const node&b)const{
return l==b.l? r<b.r : l<b.l;
}
}a[N],b[N];
queue<int> q;
void clear(){
while(!q.empty()) q.pop();
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d %d",&a[i].l,&a[i].r);
sort(a+1,a+n+1);
/* 离散化 */
for(int i=1;i<=n;i++){
if(a[i].l==a[i-1].l&&a[i].r==a[i-1].r) continue;
b[++cnt]=a[i];
}
for(int i=1;i<=cnt;i++){
if(b[i].l!=b[i-1].l) clear();
while(q.size()&&b[i].r-q.front()>=n) q.pop();
q.push(b[i].r);
ans=max(ans,(int)q.size());
}
ans=(n-ans);
printf("%d\n",ans);
return 0;
}