1. Mio visits ACGN Exhibition(线性dp+滚动数组)
题意:
输入一个二维01矩阵,求从左上角走到右下角满足如下条件的路径数:经过0的个数不小于p,经过1的个数不小于q
输入:
2 2 1 1 //n,m,p,q
0 0
1 1
输出:
2
输入:
3 3 2 0
0 0 1
0 0 1
1 0 0
输出:
6
#include<iostream>
#include<cstring>
using namespace std;
const int mod=998244353;
int arr[510][510];
int n,m,p,q;
int f[2][510][1010];//第一维滚动数组,四维优化三维,只考虑走0的情况,走1的情况相减即可得知
int main()
{
cin>>n>>m>>p>>q;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
scanf("%d",&arr[i][j]);
}
}
if(arr[1][1]==0) f[1][1][1]=1;//初始化
else f[1][1][0]=1;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
for(int k=0; k<=n+m-1; k++)
{
if(i==1&&j==1) continue;//这种情况已经初始化
if(arr[i][j]==0)
{
if(k!=0) f[i%2][j][k]=(f[(i-1)%2][j][k-1]+f[i%2][j-1][k-1])%mod;//k=0时,数组越界,这里是真坑
else f[i%2][j][k]=0;
}
else f[i%2][j][k]=(f[(i-1)%2][j][k]+f[i%2][j-1][k])%mod;
}
}
}
int cnt=0;
for(int i=p; i<=n+m-1-q; i++)//0的个数要大于p,小于等于n+m-1-q
{
cnt=(cnt+f[n%2][m][i])%mod;
}
cout<<cnt<<endl;
return 0;
}
2. Dyson Box(模拟)
题意:
有n个箱子,输入他们的坐标,这些箱子会受到两种力:向左和向下。因此他们会被推到最左边或最下边。每加入一个箱子,我们都要求当前所有箱子受到向左或者向下的力之后的周长
输入:
4
1 2
3 2
2 1
4 1
输出:
4 4
8 6
8 8
10 8
#include<iostream>
using namespace std;
int ver[200020];//x轴
int hor[200020];//y轴
int main()
{
int n;
cin>>n;
int x1,y1;
long long heng=0,shu=0;
while(n--)
{
int x,y;
scanf("%d %d",&x,&y);
ver[x]++;
hor[y]++;
x1=4;
y1=4;
if(ver[x]>1)
x1-=2;
if(hor[y]>1)
y1-=2;
if(ver[x-1]>=ver[x])
x1-=2;
if(ver[x+1]>=ver[x])
x1-=2;
if(hor[y+1]>=hor[y])
y1-=2;
if(hor[y-1]>=hor[y])
y1-=2;
heng+=x1;
shu+=y1;
printf("%d %d\n",heng,shu);
}
return 0;
}
3. Icebound and Sequence(快速幂+龟速乘)
输入:
2
2 3 100
511 4 520
输出:
14
184
对结果取余时,为了防止数爆longlong,通常在计算的过程中取余,然而过程取余会对结果相除造成影响(除非除数是模的因子)
#include<iostream>
using namespace std;
int t;
long long slow_mul(long long x,long long y,long long mod)
{
long long ans=0;
while(y)
{
if(y&1) ans=(ans+x)%mod;
x=(x+x)%mod;
y=y>>1;
}
return ans;
}
long long qmi(long long x,long long y,long long mod)
{
long long cnt=1;
while(y)
{
if(y&1) cnt=slow_mul(cnt,x,mod)%mod;
x=slow_mul(x,x,mod)%mod;
y=y>>1;
}
return cnt;
}
int main()
{
cin>>t;
while(t--)
{
int q,n,p;
cin>>q>>n>>p;
cout<<(qmi(q,n+1,(q-1)*p)-q)/(q-1)<<endl;//等比公式,注意取余的问题
}
return 0;
}
4. 舔狗(贪心)
输入:
10
3 1 8 6 10 1 4 1 6 1
输出:
0
#include<iostream>
#include<queue>
using namespace std;
int n;
int st[2000020],like[2000020];
int in[2000020];
struct node
{
int u,v;
bool friend operator<(node a,node b)//优先级队列如果插入的节点是结构体类型,则要在结构体中重载比较操作符函数。
{
return a.v>b.v;//<为从大到小排列,>为从小到大排列
}
};
int solve()
{
int ans=0;
priority_queue<node> q;
for(int i=1;i<=n;i++)
{
q.push({i,in[i]});
}
while(!q.empty())
{
node t=q.top();
q.pop();
if(st[t.u]||st[like[t.u]]) continue;//如果他或者他喜欢的人已经配对,就跳到下一次循环
ans=ans+2;
st[t.u]=1;
st[like[t.u]]=1;
q.push({like[like[t.u]],--in[like[like[t.u]]]});//将他喜欢的人的喜欢的人 入度-1 并放到优先队列中去,此时队列中会有重复的人,但因为按入度排序,并标记了是否被访问过,所以没有影响
}
return n-ans;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&like[i]);//i喜欢like[i]
in[like[i]]++;
}
cout<<solve()<<endl;
return 0;
}
5. smart robot(dfs)
题意: 给一个n*n的矩阵(1<=矩阵中的数字<=9),可以选定矩阵中任意一个点作为起点,然后可以向上左下右四个方向走任意步,每一个路径我们都会得到一串数字,我们把它转化成十进制数字,求这个矩阵不能得到的最小数字
输入:
4
1 2 3 4
3 6 7 8
0 1 5 4
9 1 1 1
输出:
17
#include<iostream>
using namespace std;
int n;
int arr[60][60];
int st[10000];
int dx[4]={0,-1,0,1};
int dy[4]={-1,0,1,0};
void dfs(int x,int y,int cnt,int sum)
{
if(cnt==5) return;
//cout<<sum<<endl;
st[sum]=1;
for(int i=0;i<4;i++)
{
int x1=x+dx[i];
int y1=y+dy[i];
if(x1>=1&&x1<=n&&y1>=1&&y1<=n)
{
dfs(x1,y1,cnt+1,sum*10+arr[x1][y1]);
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&arr[i][j]);
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
dfs(i,j,1,arr[i][j]);
}
}
for(int i=1;i<10000;i++)
{
if(!st[i])
{
cout<<i<<endl;
return 0;
}
}
return 0;
}
6. Adventurer’s Guild(二维费用背包)
题意:
给出怪物的数量n,人物血量H,人物的攻击S;
接下来n行,每一行为每只怪物的血量h,攻击s,价值w;
每消灭一个怪物,消耗h的血量和s的攻击,S如果为负数,需要用H去弥补S,如果H为负数则结束;
输出可以获得的最大价值;
输入:
2 66 22
1 23 2
66 8 90
输出:
2
输入:
4 16 22
1 23 11
5 8 14
2 36 99
15 22 27
输出:
27
#include<iostream>
using namespace std;
int n,H,S;
long long dp[310][310];//消耗血量为i,攻击为j的最大价值
int main()
{
cin >> n >> H >> S;
int ans = 0;
for(int i = 1; i <= n; i ++)
{
int h,s,w;
cin >> h >> s >> w;
for(int j = H; j > h; j --)
{
for(int k = S; k >= 0; k --)
{
if(j + k > h + s && j > h)
{
if(s > k)//攻击不够,血量补
{
int x = h + s - k;
dp[j][k] = max(dp[j][k],dp[j - x][0] + w);
}
else//攻击够
{
dp[j][k] = max(dp[j][k],dp[j - h][k - s] + w);
}
}
}
}
}
cout << dp[H][S] << endl;
return 0;
}
7. Matrix Problem(构造)
题意:
给定一个n×m的01矩阵C,要求输出两个相同大小的01矩阵A,B,使得A,B重合为1的地方C为1,其余地方的C为0
输入:
5 5
00000
00100
01010
01100
00000
输出:
11110
10100
11110
11100
11110
00001
01111
01011
01111
00001
#include<iostream>
using namespace std;
int n,m;
char a[510][510];
int main()
{
cin>>n>>m;
getchar();
for(int i=0;i<n;i++)
{
cin>>a[i];
}
for(int i=0;i<n;i++)
{
cout<<"1";
if(i%2==0)//偶行全为1
{
for(int j=1;j<m-1;j++) cout<<"1";
}
else
{
for(int j=1;j<m-1;j++) cout<<a[i][j];
}
cout<<"0"<<endl;
}
for(int i=0;i<n;i++)
{
cout<<"0";
if(i%2==0)
{
for(int j=1;j<m-1;j++) cout<<a[i][j];
}
else//奇行全为1
{
for(int j=1;j<m-1;j++) cout<<"1";
}
cout<<"1"<<endl;
}
return 0;
}
8. Longest Continuous 1
题意:
t组数据,每次输入一个n,表示取01串的前n个数,求这前n个数中最长连续1的长度
#include<iostream>
using namespace std;
int t,k;
int main()
{
cin>>t;
while(t--)
{
cin>>k;
if(k==1)
{
cout<<"0"<<endl;
}
else
{
int sum=1,ans=1;
for(int i=1; i<=60; i++)
{
sum+=(1<<(i-1))*i;
if(k>=sum+1) ans=i+1;//注意这里的细节,sum+1,sum是加完某一个相同长度的二进制数位后的值,而+1是因为下一个二进制数最高位必然为1
else break;
}
cout<<ans<<endl;
}
}
return 0;
}
9. String Game(dp,类似最长公共子序列)
题意:
给定一个字符串和它的子序列,问这个子序列按顺序可以匹配到原字符串中多少个不同得位置
输入:
eeettt
输出:
9
输入:
eeettt
输出:
0
#include <bits/stdc++.h>
using namespace std;
const int M = 1e9 + 7;
char a[5005], b[1005];
int dp[1005][5005]; //第一位存已经匹配到的数位(子序列),第二位存当前进行到的数位(原序列)
int n, m, i, j;
int main()
{
scanf("%s%s", a + 1, b + 1);
memset(dp, 0, sizeof(dp));
n = strlen(a + 1);
m = strlen(b + 1);
for (i = 1; i <= m; i++)
{
for (j = 1; j <= n; j++)
{
if (a[j] == b[i])
{
if (i == 1)
dp[i][j] = (1 + dp[i][j - 1]) % M;
else
dp[i][j] = (dp[i - 1][j - 1] + dp[i][j - 1]) % M;
}
else
{
dp[i][j] = dp[i][j - 1];
}
}
}
printf("%d\n", dp[m][n]);
return 0;
}
10. Kobolds and Catacombs
题意:
给定一个数字序列,要求给它划分区间,然后在区间内排序,使该序列成为一个不减序列,问最多可以划分成多少个区间
输入:
5
1 3 2 7 4
输出:
3
O(nlog n)
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int a[1000010],b[1000010];
long long sum1,sum2;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
int cnt=0;
for(int i=1;i<=n;i++)
{
sum1+=a[i];
sum2+=b[i];
if(sum1==sum2) cnt++;
}
cout<<cnt<<endl;
return 0;
}
O(n)
#include<iostream>
using namespace std;
int n;
int a[1000010],b[1000010];//b存最小后缀,不包括当前位置
int cnt;
int mina=1e9+5;
int maxa;
int main()
{
cin>>n;
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int i=n-1;i>=0;i--)
{
b[i]=mina;
mina=min(mina,a[i]);
}
for(int i=0;i<n;i++)
{
maxa=max(maxa,a[i]);
if(maxa<=b[i]) cnt++;
}
cout<<cnt<<endl;
return 0;
}
11. Build Roads
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
long long n,L,R;
int a[200020];
long long seed;
int p[200020];
int m;
long long xorshift64()
{
long long x=seed;
x^=x<<13;
x^=x>>7;
x^x<<17;
return seed=x;
}
int gen()
{
return xorshift64()%(R-L+1)+L;
}
struct Edge
{
int a,b,w;
bool operator< (const Edge &W)const
{
return w<W.w;
}
}edges[200020];
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
long long k()
{
long long res=0;
sort(edges,edges+m);
for(int i=1; i<=n; i++) p[i]=i;
for(int i=0; i<m; i++)
{
int a=edges[i].a;
int b=edges[i].b;
int w=edges[i].w;
a=find(a);
b=find(b);
if(a!=b)
{
p[a]=b;
res+=w;
}
}
return res;
}
signed main()
{
scanf("%lld%lld%lld%lld",&n,&L,&R,&seed);
for(int i=1; i<=n; i++)
{
a[i]=gen();
}
if(L==R)
{
cout<<1ll*L*(n-1)<<endl;
return 0;
}
if(n>7)
{
cout<<n-1<<endl;
return 0;
}
if(n<=7)
{
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
edges[m++]={i,j,__gcd(a[i],a[j])};
}
}
cout<<k()<<endl;
}
return 0;
}