L1-1 签到
void solve()
{
cout<<"huan ying lai dao nan nang shi fan xue yvan ACM shi yan shi.";
}
L1-2 签到
void solve(){
string s;
int ans=0;
cin>>s;
for(int i=0;i<s.size();i++){
if(s[i]=='.') {continue;}
ans=ans*10+s[i]-'0';
}
cout<<ans;
}
L1-3 签到
void solve(){
int res,n;
cin>>res>>n;
string z="你所热爱的就是你的生活.\n";
string y="Spongebob, here I come!\n";
while(n--){
int k;
cin>>k;
if(k<=res) cout<<y;//可以买
else cout<<z;
}
}
L1-4 签到 注意输出答案开longlong
int n,z,y;
long long ans;
void solve(){
y=9999;
cin>>n;
while(n--){
int s;cin>>s;
s=min(s,y);
ans+=s;
}
cout<<ans;
}
L1-5 按题意模拟
void solve(){
int j,p,x1,x2;
cin>>j>>p>>x1>>x2;
//俩人都可以进,并且其中一个年龄小于禁入年龄线
if((x1<j&&x2>=p)||(x1>=p&&x2<j)){
cout<<x1<<"-Y "<<x2<<"-Y\n";
if(x1>x2)
cout<<"qing 1 zhao gu hao 2\n";
else
cout<<"qing 2 zhao gu hao 1\n";
return;
}
//俩人都可以进,并且年龄都>=禁入年龄线
if(x1>=j&&x2>=j){
cout<<x1<<"-Y "<<x2<<"-Y\n";
cout<<"huan ying ru guan\n";
return;
}
//只有一个人可以进入
if(x1>=j){
cout<<x1<<"-Y "<<x2<<"-N\n";
cout<<"1: huan ying ru guan\n";
return;
}
if(x2>=j){
cout<<x1<<"-N "<<x2<<"-Y\n";
cout<<"2: huan ying ru guan\n";
return;
}
//都无法进入
cout<<x1<<"-N "<<x2<<"-N\n";
cout<<"zhang da zai lai ba\n";
}
L1-6 签到
#define ld long double
int n;
ld res,ts;
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
ld s;cin>>s;
ts=1.00/(s);
res+=ts;
}
res=1.00/(res*1.0/n);
//保留两位小数
cout<<fixed<<setprecision(2)<<res;
}
L1-7签到
int n,tz,ty;
void solve(){
cin>>n;
string z="You are lucky!\n";
string y="Wish you good luck.\n";
while(n--){
string s;
cin>>s;
tz=0,ty=0;
for(int i=0;i<s.size();i++){
if(i<3) tz+=s[i]-'0';
else ty+=s[i]-'0';
}
if(tz==ty) cout<<z;
else cout<<y;
}
}
L1-8 模拟
int a[8][8];
void solve(){
for(int i=1;i<=6;i++){
int k;cin>>k;
a[i][k]=1;//第i个骰子,第k数字已出现
}
cin>>n;
/*
每个骰子摇出的点数都跟它之前任何一次出现的点数不同,
并且要求每次得到的点数最大,
所以基本策略就是每次都要投出最大的点数,
即从6开始,但出现过的点数不能再次出现*/
for(int i=1;i<=6;i++){//第i个骰子
int k=n;
for(int j=6;j>=1;j--){//从6开始投K次
if(a[i][j]) {continue;}
k--;
if(k==0) {cout<<j;break;}
}
if(i!=6) cout<<" ";//行首位不得有多余空格。
}
}
L1-9 (灵活运用string模板库里的函数)
相关知识
s.substr(a, b);
//在s中下标为a开始的b个字符,
//包括a,如果b大于s中a下标开始后面的元素个数,则只需到末尾。
s.erase(a); //删除下标a以及a之后的字符
s.erase(a, b); //删除从下标a开始包括下标a在内的b个字符
string s1("Hello World");
s1.find("lo"); //在s1中从前向后查找"lo"第一次出现的地方,
//如果找到,返回"lo"开始的位置,即 'l' 所在的位置下标;
//如果找不到,返回string::npos(string中定义的静态常量 int类型时为-1)
s1.find("ll", 1); //从下标为1的地方开始往后找
s1.rfind("lo"); //从后向前查找,返回 'l' 所在位置的下标
s1.find_first_of("abcd"); //在s1中从前向后找,"abcd"中任何一个字符第一次出现的地方。
s1.find_last_of("abcd"); //最后一次出现的地方,即从后往前找第一次出现的地方。
s1.find_first_not_of("abcd"); //不在"abcd"中的字母第一次出现的地方。
s1.find_last_not_of("abcd"); //不在"abcd"中的字母最后一次出现的地方。
#include<bits/stdc++.h>
using namespace std;
int main(){
string s, s1, s2;
cin >> s;
int n, a, b;
cin >> n;
while(n--){
cin >> a >> b >> s1 >> s2;
a--; b--; //由于字符串数组的下标是从0开始,即需要减一
string cp = s.substr(a, b-a+1), ss = s1 + s2; //ss用来判断是否有插入位置的
s.erase(a, b-a+1); //将剪切位置的内容删掉
int ii = s.find(ss); //找插入位置是否存在
if(ii == -1) //如果没找到,s.find()返回-1
s += cp; //+:代表拼接字符串,s = s + cp;即将剪切板上的字符拼接到s后面
else{ //如果找到,s.find()返回找到子串位置的第一个字符的地方
string tmp = s.substr(0, ii); //插入位置前的子串
tmp += s1; //将插入位置前的标志子串插入
tmp += cp; //将剪切的字符串插入
tmp += s.substr(ii+s1.length(), s.length()-ii-s1.length()); //插入后面还剩下的
s = tmp;
}
}
cout << s << endl;
return 0;
}
L1-10
int n,m;
int cnt;//统计做过的不简单的题个数
void solve(){
cin>>n>>m;
getchar();
for(int i=0; i<n; i++)
{
string s;
getline(cin,s);
if(s.find("qiandao")==s.npos&&s.find("easy")==s.npos) //判断是否含有“easy"与"qiandao"
{
cnt++;
if(cnt==m+1)//如果吉老师做到了已做过的题的下一个
{
cout<<s<<endl;//这是吉老师正在做的题
}
}
}
if(cnt<=m)//在这个范围内就AK;
cout<<"Wo AK le\n";
}
L1-11
#include<bits/stdc++.h>
using namespace std;
char _map[1001][1001];//_map数组保存地图
int flag[1001][1001],a[1000001];//a数组要开大一点,刚开始开a[1001]错了3个点
//flag数组保存各个点所在的连通图,以及是否已经处理过,a数组保存各个连通图的大小
struct mg
{
int x,y;
}q[1000001];
int main()
{
int sx,sy,i,j,n,m,l,nx,ny,k,f,r,sum,d;
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};//四个方向
scanf("%d %d",&n,&m);//n是正方形地图边长,m是数据组数
memset(a,0,sizeof(a));
memset(flag,0,sizeof(flag)); //你可以无视这两行memset
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cin>>_map[i][j];//读入地图
d=0;//d用来保存当前是在第几个连通图中
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(flag[i][j]==0)//如果当前位置不在已知连通图中(还未处理过)
{
d++;//记录当前所在连通图数
f=1;
r=1;
q[f].x=i;
q[f].y=j;
flag[i][j]=d;
sum=1;//初始化
while(f<=r)
{
for(k=0;k<4;k++)
{
nx=q[f].x+dx[k];
ny=q[f].y+dy[k];
if(flag[nx][ny]==0&&nx>=1&&nx<=n&&ny>=1&&ny<=n&&((_map[nx][ny]=='1'&&_map[q[f].x][q[f].y]=='0')||(_map[nx][ny]=='0'&&_map[q[f].x][q[f].y]=='1')))
//如果新位置能走且在地图上
{
r++;
sum++;//计数器累加
flag[nx][ny]=d;//标记新位置在第d个连通图中
q[r].x=nx;
q[r].y=ny;//更新位置
}
}
f++;
}
a[d]=sum;//保存当前连通图能移动到多少格
}
for(i=1;i<=m;i++)
{
cin>>sx>>sy;//读入询问
cout<<a[flag[sx][sy]]<<endl;//直接查找答案并输出
}
return 0;
}
L1-12
解题:
(1)第一部分输入的是 0-9 的数字,其中 0 表示我们已知的数字,然后依次输入 9 的数字,一定会有一个数字没有被输入,这个数字就是我们已知的数字的值;
(2)其次需要输入坐标,来刮数字,并且输出所刮数字;
(3)然后需要输入选择:1-3 表示行相加;4-6表示列相加;7表示主对角线相加;8表示副对角线;
(4)最后我们需要根据选择计算和,然后找到对应的金币。
易错点提示:
(1)记得把 0 替换为该位置的值:用一个数组 flag 来存数,输入的数标记为 1,没有输入的数标记为 0,寻找标记为 0 的数,即为已知的数;
(2)输入坐标的同时输出该坐标的数,只需要用二维数组来存放所有数字即可;
(3)输入选择并求和,用 if 把所有情况考虑到即可;
(4)换算金币时记得 sum - 6;
int n,m;
int a[5][5];
int b[5][5];
int c[10];
map<int,int>s;
void solve()
{ n=3;
s[6]=10000;
s[7]=36;
s[8]=720;
s[9]=360;
s[10]=80;
s[11]=252;
s[12]=108;
s[13]=72;
s[14]=54;
s[15]=180;
s[16]=72;
s[17]=180;
s[18]=119;
s[19]=36;
s[20]=306;
s[21]=1080;
s[22]=144;
s[23]=1800;
s[24]=3600;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
cin>>a[i][j];
b[i][j]=a[i][j];
c[a[i][j]]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(a[i][j]==0) {
for(int k=1;k<=9;k++) {
if(c[k]) continue;
b[i][j]=k;
break;
}
}
}
int ans=0;
for(int i=1;i<=3;i++){
int z,y;
cin>>z>>y;
b[z][y]=a[z][y];
cout<<b[z][y]<<endl;
}
cin>>n;
if(n<=3){
for(int i=1;i<=3;i++) ans+=b[n][i];
}
else if(n<=6){
for(int i=1;i<=3;i++) ans+=b[i][n-3];
}
else{
if(n==7) {
ans+=b[1][1]+b[2][2]+b[3][3];
}else
ans+=b[1][3]+b[2][2]+b[3][1];
}
cout<<s[ans];
}
L1-13
const int N=100000;
struct node{
int l,r;
}tr[N];
int n;
int m;
string s;
void solve()
{cin>>n>>m;
//等比数列求和公式,一共n行,每行是前一行二倍
n=(1-pow(2,n))/(1-2);
while(m--){
cin>>s;
int u=1;//u所走的位置
for(int i=0;i<s.size();i++){
if(s[i]=='y') u=u*2;//左下走
else u=u*2+1;//右下走
}
//答案第(n+1)行下标-前n行总个数
cout<<u-n<<endl;
}
}
L1-14
根据题意可以进行三次分类
1、天梯赛成绩小于175的直接刷掉
2、天梯赛成绩大于等于175且pta成绩>=s,不用考虑有成绩重复的
3、天梯赛成绩大于等于175且pta成绩<s,把这个分数的人数统计一下,与批次s比较
int n,s,k,a[300];//用于记录同一个分数有多少人
void solve(){
cin>>n>>k>>s;
int ans=0;
while(n--){
int t,p;
cin>>t>>p;
if(t>=175)//根据1,刷掉天梯赛成绩小于175的同学学
{
if(p>=s)//根据3,天梯赛>=175&& pat>=s
ans++;
else//根据3,记录天梯赛成绩为t的同学有多少人
{
a[t]++;
}
}
}
for(int i=175;i<=290;i++){
ans+=min(a[i],k);//根据2,求出同一个分数段的同学有多少可以录用
}
cout<<ans;
}
L1-15
L1-999 (数论) 板子题
因为p不一定是质数,因此卡快速幂求逆元,
可以使用扩展欧几里得求逆元
线性递推求逆元
设 t = p / i,k = p % i,有:p = i * t + k
即 i * t + k Ξ 0 (mod p)
即 k Ξ - i * t (mod p)
两边同时除以 i * k
有 1 / i Ξ - t / k (mod p)
将k,t带入
有 inv[ i ] Ξ - p / i * inv[ p % i ] (mod p)
为防止有负数,有inv[ i ] = ( p - p / i * inv[ p % i ] % p ) % p
划重点:inv[ i ] = ( p - p / i * inv[ p % i ] % p ) % p
注意:【每个数对于模p的逆元不同】
- exgcd和费马小定理只适合用来求单个逆元,求3e6以内所有的逆元肯定会超时,所以这里要用线性递推求逆元,这样可以保证时间复杂度在O( n )内
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define int long long
//扩展欧几里得求逆元
int exgcd(int a,int b,int &x,int &y){
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int inverse(int a,int p){
int x=0,y=0;
int res=exgcd(a,p,x,y);
if(res==1) res=(x+p)%p;
else{res=-1;}
return res;
}
int inv[3000010];
int l,r;
//线性递推求逆元
void incerse_e(int n,int p){
int res=0;
inv[1] = 1;
for(int i=2;i<=n;i++){
inv[i] = p - (p / i * inv[p % i] % p) % p;
}
int q;
cin>>q;
while(q--) {
res=0;
cin>>l>>r;
for(int i=l;i<=r;i++){
res=(res%p+inv[i]%p)%p;
}
cout<<res<<endl;
}
}
void solve(){
int ok,n,p;
cin>>ok>>n>>p;
if(ok==1){
int ti=inverse(n,p);
if(ti==-1) cout<<"impossible\n";
else cout<<ti<<endl;
return;
}
incerse_e(n,p);
}
signed main(){
int t;cin>>t;
while(t--){
solve();
}
return 0;
}
L2-1
L2-2
L2-3
L2-4
L2-5(树形dp)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010, M = N * 2;// N个顶点至多2*N条有向边
int n;
int w[N];// 节点的权值
int h[N], e[M], ne[M], idx;
// h是顶点集,e[i]=b表示a指向b(一条边),ne[i]表示结点i的next的指针,idx指向当前需要插入(已经用过)的结点
LL f[N];// 在以u为根的子树中包含u的所有连通块的权值的最大值
void add(int a, int b)// 这里采用数组实现邻接表来存储图,也就是将多个单链表h[i]拼起来
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;// 头插法创建单链表,新节点指向已有节点
// h[a]是单链表a的起点,最后一个插入元素的地址,也是idx区间的终点
}
void dfs(int u, int father)// 求f[i],第二个参数记录父节点,防止往回走
{
f[u] = w[u];
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (j != father)
{
dfs(j, u);
f[u] += max(0ll, f[j]);// long long的0,和0比较一下,如果<=0没必要加上
}
}
}
int main()
{
scanf("%d", &n);
memset(h, -1, sizeof h);// 记得h数组置-1
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
for (int i = 0; i < n - 1; i ++ )// 一共n-1条边
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);// 构建无向图
}
dfs(1, -1);
LL res = f[1];
for (int i = 2; i <= n; i ++ ) res = max(res, f[i]);// 求f[1]到f[n]的max
printf("%lld\n", res);
return 0;
}
L3-1
biset板子题(会用的话,纯签到)
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
#define int long long
int a[10010];
int n,m;
void solve()
{ cin>>n>>m;
vector<int>w;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
int ans=0;
for(int i=1;i<=n;i++){
int ti=ans+a[i];
if(ans==m||ti>m) break;
int tk=m-ti;
bitset<(int)110> vis;//二进制
//如果i+1到n全部的数据,任意凑数
//可以凑出来X则vis[x]=1
vis[0] = 1;
for (int j = i+1; j <= n; j++) {
vis |= vis << a[j];
}
if(vis[tk]) {w.push_back(a[i]);ans+=a[i];
}
}
if(ans!=m) cout<<"No Solution";
else {
for(auto k : w) {
cout<<k;
if(k!=*(w.end()-1)) cout<<" ";
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
solve();
return 0;
}
爆搜YYDS:
因为M很小,才100,所以剪枝的作用很大,排序以后爆搜就行
注意特判一下这n个数的和与m的关系,如果小于m,则一定不可以凑出来,直接输出就行,不然会TLE在最后一个点
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;
#define MAX 300000 + 50
int n, m, k;
int tr[MAX];
vector<int>ans, a;
bool p;
void dfs(int id, int x){
if(x > m || p || id > n + 1)return;
if(x == m){
p = 1;
a = ans;
return;
}
for(int i = id; i <= n; ++i){
if(p)break;
ans.push_back(tr[i]);
dfs(i + 1, x + tr[i]);
ans.pop_back();
}
}
void work(){
cin >> n >> m;
int sum = 0;
for(int i = 1; i <= n; ++i){
cin >> tr[i];
sum += tr[i];
}
if(sum < m){
cout << "No Solution\n";
return;
}
sort(tr + 1, tr + 1 + n);
for(int i = 1; i <= n; ++i){
if(tr[i] > m){
n = i - 1;
break;
}
}
dfs(1, 0);
if(!p)cout << "No Solution\n";
else {
for(int i = 0; i < a.size(); ++i){
if(i)cout << " ";
cout << a[i];
}
cout << endl;
}
}
int main(){
io;
work();
return 0;
}
「01背包 + 输出路径」
因为要字典序最小的,所以我们可以从大到小排个序,这样更新的时候是值小的后更新,输出的时候就是字典序最小的
dp[i][j]表示前i个,花费价值为j的钱能得到的最大价值
开一个vis[i][j]数组,为1则表示前i个物品,体积为j时可以选i这个物品使的价值得到最大,更新i的时候,判断一下dp[i][j]与dp[i-1][j - ar[i]]+ar[i]的大小,如果能更新的话,就让vis[i][j] = 1
输出路径的时候就倒着输出,看当前体积v和当前id的vis是否是1,是的话就选这个物品,并更新体积
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;
#define MAX 10000 + 50
int n, m, k, x;
int tr[MAX];
int dp[MAX];
bool vis[MAX][105];
void work(){
cin >> n >> m;
for(int i = 1; i <= n; ++i)cin >> tr[i];
sort(tr + 1, tr + 1 + n, greater<int>());
for(int i = 1; i <= n; ++i){
for(int j = m; j >= tr[i]; --j){
if(dp[j] <= dp[j - tr[i]] + tr[i]){
dp[j] = dp[j - tr[i]] + tr[i];
vis[i][j] = 1;
}
}
}
if(dp[m] == m){
vector<int>ans;
int id = n;
while (m) {
if(vis[id][m]){
ans.push_back(tr[id]);
m -= tr[id];
}
--id;
}
for(int i = 0; i < ans.size(); ++i){
if(i)cout << " ";
cout << ans[i];
}
cout << endl;
}
else cout << "No Solution\n";
}
int main(){
io;
work();
return 0;
}
L4-1???
问题转移为: 有n个完全不同的球,分给m个不同的人
#include<iostream>
#include<bits/stdc++.h>
#include<math.h>
#define int long long
#define endl '\n'
const int N=1e6+5;
const int mod=1e9+7;
using namespace std;
int fz[N],fm[N];
int fpw(int x,int n)//快速幂
{
int s=1;
while(n)
{
if(n%2==1) s=s*x%mod;
x=x*x%mod;
n=n/2;
}
return s;
}
int inv(int x)//逆元
{
return fpw(x,mod-2);
}
void intv()//初始化1~N阶乘的逆元
{
int i,j;
fz[0]=fm[0]=1;
for(i=1;i<N;i++)
fz[i]=fz[i-1]*i%mod;
fm[N-1]=fpw(fz[N-1],mod-2);
for(j=N-2;j>=1;j--)
fm[j]=fm[j+1]*(j+1)%mod;
}
int c(int n,int k)//求C(n,k)
{
if(n<k) return 0;
return fz[n]*fm[k]%mod*fm[n-k]%mod;
}
signed main()
{
int i,j,t,k,m,n;
cin>>n>>m;
intv();
int sum=0;k=1;
for(i=0;i<=m;i++)
{
if(i%2==0)
sum+=c(m,i)*fpw(m-i,n)%mod;
else
sum+=mod-c(m,i)*fpw(m-i,n)%mod;//如果sum直接加上-c(m,i)*fpw(m-i,n)%mod会有可能变为负数,所以加上个mod保证其为正数
sum=sum%mod;
}
sum=sum*fm[m]%mod;
cout<<sum<<endl;
}