2021-2-22 个人赛
A题 CodeForces 1005B
简单思维
两个字符串 从左边删除 问一个删除多少个后两个字符串相同(或者空)
思路就是从后面遍历 找出两个字符串相同的后缀大小 然后用两个字符串总长度去减
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int main()
{
char s1[200001], s2[200001];
int l1, l2, i, j, res;
scanf("%s%s", s1, s2);
l1 = strlen(s1);
l2 = strlen(s2);
res = l1 + l2;
for (i = l1 - 1, j = l2 - 1; i >= 0 && j >= 0; i--, j--)
{
if (s1[i] == s2[j])
{
res -= 2;
}
else
{
break;
}
}
printf("%d\n", res);
return 0;
}
B题 CodeForces 1042A
简单思维和贪心
#include<iostream>
using namespace std;
int main(){
int a[101];
int n,m;
cin>>n>>m;
int max=0;
for(int i=0;i<n;i++){
cin>>a[i];
if(a[i]>a[max])max=i;
}
int x;
int min=0;
x=a[max]+m;
while(m--){
for(int i=0;i<n;i++){
if(a[i]<a[min])min=i;
}
a[min]=a[min]+1;
}
int max1=0;
for(int i=0;i<n;i++){
if(a[i]>a[max1])max1=i;
}
int y;
y=a[max1];
printf("%d %d\n",y,x);
return 0;
}
C题 CodeForces 1153A
简单思维
#include<iostream>
using namespace std;
const int N=100010;
int a[N],b[N];
int main(){
int n,t;
cin>>n>>t;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i];
}
int max=1;
for(int i=1;i<=n;i++){
if(a[i]<t){
while(a[i]<t){
a[i]+=b[i];
}
}
}
for(int i=1;i<=n;i++){
if(a[i]>=t){
if(a[i]<a[max])
max=i;
}
}
printf("%d\n",max);
}
D题 CodeForces - 1153B
#include<bits/stdc++.h>
#define llINF 9223372036854775807
#define pi 3.141592653589793//23846264338327950254
#define ll long long
using namespace std;
const ll maxn=1e3+7;
const double eps=1e-10;
const ll mod=1e9+7;
//对于每个位置的数字,他只能取所在行规定的最大值maxr和所在列规定的最大值maxc中更小的那个,也就是min(maxr,maxc)
//并且我们取到这个最大值是满足构造条件的
//因此我们直接让每个位置都为这个位置可以存放的最大值就可
int main()
{
ll n,m,h;
ll field[maxn][maxn];
ll maxr[maxn],maxc[maxn];
cin>>n>>m>>h;
for(ll i=1;i<=m;i++) cin>>maxc[i];
for(ll i=1;i<=n;i++) cin>>maxr[i];
for(ll i=1;i<=n;i++)
{
for(ll j=1;j<=m;j++)
{
cin>>field[i][j];
if(field[i][j]) field[i][j]=min(maxr[i],maxc[j]);
}
}
for(ll i=1;i<=n;i++)
{
for(ll j=1;j<=m;j++)
{
if(j>1) cout<<' ';
cout<<field[i][j];
}
cout<<endl;
}
}
E题 POJ 3278
本题两种思路,可用bfs和dp来解决。
算是bfs模板题。
下面为bfs的做法
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 1e5+10;
queue <int> q;
int step[MAXN];
int vis[MAXN];
int BFS(int n, int k)
{
int head, np;
q.push(n);
step[n] = 0;
vis[n] = 1;
while(!q.empty())
{
head = q.front();
q.pop();
for (int i = 0; i < 3; i++)
{
if (i == 0) np = head * 2;
else if (i == 1) np = head -1;
else np = head +1;
if (np < 0 || np >= MAXN || vis[np]) continue;
q.push(np);
vis[np] = 1;
step[np] = step[head] + 1;
if (np == k)
return step[np];
}
}
return 0;
}
int main()
{
int n, k;
cin >> n >> k;
if (n >= k)
cout << n-k << endl;
else
cout << BFS(n, k) << endl;
return 0;
}
考虑四个状态。p点本身,p-1的最优+1,p+1的最优+1,p/2的最优+1(对于偶数的点)
首先,以N为中心,i为从0到K的变量。则从i到N的最普通的方法就是进行平移,所以给初始DP数组赋值为距离。
接下来,对于奇数的,只考虑前3个。由于p+1的最优只有可能是dp[i+1](由于从小到大遍历,此时还没有变化,是最基本的值。)和dp[(i+1)/2]+1,所以将这两个都考虑进来。又由于dp[i+1]=dp[i]+1,所以不再考虑dp[i+1]。同时,也不考虑dp[p/2]+1。
对于偶数,比上述的多考虑一个dp[p/2]+1就完事了。
下面是dp解法
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int N,K;
const int MAX = 100005;
int dp[MAX];
int main(){
cin>>N>>K;
for(int i = 0;i <= K;i++){
dp[i] = abs((int)(N - i));
}
for(int i=N+1;i<=K;i++){
if(i & 1){//奇数
dp[i]=min(dp[i],dp[(i+1)/2]+2);
dp[i]=min(dp[i],dp[i-1]+1);
//由于此时没有更新i+1的东西,所以dp[i+1]-1=dp[i]
}
else{
dp[i]=min(dp[i],dp[i/2]+1);
dp[i]=min(dp[i],dp[i-1]+1);
dp[i]=min(dp[i],dp[(i+1)/2]+2);
}
}
cout<<dp[K]<<endl;
return 0;
}
F题 HDU2063
匈牙利算法模板题
注意每组样例要记得初始化数组。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 510 , M = 200010;
int h[N],ne[M],e[M],idx;
int g[N];
bool st[N];
int k,n,m;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void init()
{
memset(h, -1, sizeof h);
memset(g,0,sizeof g);
idx = 0;
}
bool find(int x)
{
for(int i=h[x];i!=-1;i=ne[i])
{
int j=e[i];
if(!st[j])
{
st[j]=true;
if(g[j]==0 || find(g[j]))
{
g[j]=x;
return true;
}
}
}
return false;
}
int main()
{
while(~scanf("%d",&k) && k){
init();
scanf("%d%d", &m, &n);
int a, b;
for(int i=0;i<k;i++)
{
scanf("%d%d", &a, &b);
add(a, b);
}
int res = 0;
for (int i = 1; i <= m; i ++ )
{
memset(st, false, sizeof st);
if (find(i)) res ++ ;
}
printf("%d\n", res);
}
return 0;
}
G题 POJ 3641
给出一个数,判断其是否是伪素数。一个数是伪素数满足下面两个条件:
1,p不是素数。 2,满足费马定理a^p==a(mod p)
考查一个判断素数和快速幂的知识点。
#include <iostream>
using namespace std;
typedef long long ll;
bool testPrime(ll x) {
for (ll i=2; i*i<=x; i++)
if(x % i == 0)
return false;
return true;
}
//快速幂运算
ll mod_pow(ll x, ll n, ll mod) {
ll res = 1;
while(n > 0) {
if(n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
ll a, p;
int main() {
while(~scanf("%lld%lld", &p, &a) && a && p) {
if(testPrime(p)) {
printf("no\n");
} else {
if(mod_pow(a, p, p) == a % p)
printf("yes\n");
else
printf("no\n");
}
}
return 0;
}
H题 CodeForces 106C
多重背包dp板子题
#include<bits/stdc++.h>
#define llINF 9223372036854775807
#define ll long long
using namespace std;
const ll maxn=1e3+7;
const double eps=1e-10;
const ll mod=1e9+7;
//挺裸的一个多重背包dp
ll w[maxn],v[maxn],num[maxn];
//w[i]代表制作一份第i种糕点需要的面粉数量,v[i]为售出一份第i种糕点获得的收益
//num[i]为第i种糕点从馅料角度看能制作的最大份数,用ai/bi即可得到
int main()
{
ll n,m,c0,d0;
cin>>n>>m>>c0>>d0;
ll a,b;
for(ll i=1;i<=m;i++)
{
cin>>a>>b;
cin>>w[i]>>v[i];
num[i]=a/b;
}
w[m+1]=c0;v[m+1]=d0;num[m+1]=llINF; //增加第m+1种糕点,也就是无馅料的糕点,制作上限为无穷个
ll dp[13][maxn]; //dp[i][j]代表制作前i种糕点,使用j份面团获得的最大收益
memset(dp,0,sizeof(dp));
for(ll i=1;i<=m+1;i++) //i是当前制作的糕点种类
{
for(ll j=0;j<=n;j++) //j是当前的初始面团份数
{
for(ll k=0;j-k*w[i]>=0&&k<=num[i];k++) //k是当前糕点制作多少份,j-k*w[i]>=0保证了面团够用,k<=num[i]保证了馅料够用
{
dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]); //多重背包的状态转移方程自己去看书或者别人博客吧.
}
}
}
cout<<dp[m+1][n]<<endl;
}
I题 CodeForces 294C
把每关一次灯看做成一种操作,那么显然要操作的次数等同于灭着的灯的次数。
然后是,把每盏亮着的灯看成是分割线,那么就可以把所有灭着的灯变成一个个的区间(长度可以为0),每个区间的长度为 a [ i ] − a [ i − 1 ] − 1,那么要使得这个区间的灯全部变量,就需要在这个区间上操作 a [ i ] − a [ i − 1 ] − 1 次。
除了最后一次操作,你每次都可以选择关上这个区间左边的灯或关上这个区间右边的灯,那么对于每个区间就有 2 ^(len − 1)
种不同的方法来把这个区间的灯全部打开。
位于两边的区间是特别的,在最左边的区间只能从右边操作,而在最右边的区间只能从左边操作,(如果没有,则视为是长度为0的区间)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1007;
const int mod=1e9+7;
int a[maxn];
ll fac[maxn],c[maxn][maxn];
ll posmod(ll a){
return (a%mod+mod)%mod;
}
void calfac(){//fac[i]表示2^{i-1}次方,0除外
fac[0]=fac[1]=1;
for(int i=2;i<maxn;i++)
fac[i]=posmod(fac[i-1]*2);
}
void calc(){//递推求组合数
c[0][0]=1;
for(int i=1;i<maxn;i++){
c[i][0]=1;
for(int j=1;j<=i;j++)
c[i][j]=posmod(c[i-1][j]+c[i-1][j-1]);
}
}
int main(){
int x,y;
calfac();
calc();
while(cin>>x>>y){
for(int i=1;i<=y;i++)
cin>>a[i];
sort(a+1,a+y+1);
int left=x-y;
ll ans=1;
for(int i=1;i<=y;i++){//最后一个区间肯定是C_{x}^{x}的情况,就省去了
ans=posmod(ans*c[left][a[i]-a[i-1]-1]);
left-=a[i]-a[i-1]-1;
}
for(int i=2;i<=y;i++)
ans=posmod(ans*fac[a[i]-a[i-1]-1]);
cout<<ans<<endl;
}
}
J题 CodeForces - 1244D
#include<bits/stdc++.h>
#define llINF 9223372036854775807
#define mp make_pair
#define pb push_back
#define ll long long
using namespace std;
const ll maxn=1e5+7;
const double eps=1e-10;
const ll mod=1e9+7;
//首先需要推一下无法染色的情况,其实就是某一个点存在三条或更多的边的时候,必然会出现染色冲突的情况(自己画图证明)
//再由任意三个相邻的点颜色必不同,可以推出整棵树其实把所有点分成三个部分,每个部分的所有点颜色必然是相同的
//之后我们可以从某一个叶子结点出发dfs,把整个树的点分割成三个部分,再枚举这三个部分对应颜色计算即可
vector<ll>field[maxn];
ll val[3][maxn];
ll cas[maxn];
ll color[6][3]=
{
1,2,3,
1,3,2,
2,1,3,
2,3,1,
3,1,2,
3,2,1
};
void dfs(ll now,ll ope)
{
if(cas[now]==-1)
{
cas[now]=ope;
ope=(ope+1)%3;
for(ll i=0;i<field[now].size();i++)
dfs(field[now][i],ope);
}
}
int main()
{
ll n;
cin>>n;
for(ll i=1;i<=n;i++) cin>>val[0][i];
for(ll i=1;i<=n;i++) cin>>val[1][i];
for(ll i=1;i<=n;i++) cin>>val[2][i];
bool flag=1;
for(ll i=1;i<n;i++)
{
ll u,v;
cin>>u>>v;
field[v].pb(u);
field[u].pb(v);
if(field[v].size()>2) flag=0;
if(field[u].size()>2) flag=0;
}
if(flag)
{
ll ans=llINF;
ll tar=-1;
for(ll i=1;i<=n;i++)
{
if(field[i].size()==1) tar=i;
cas[i]=-1;
}
dfs(tar,0);
ll col=-1;
for(ll i=0;i<6;i++)
{
ll temp=0;
for(ll j=1;j<=n;j++)
temp+=val[color[i][cas[j]]-1][j];
if(temp<ans)
{
ans=temp;
col=i;
}
}
cout<<ans<<endl;
for(ll i=1;i<=n;i++)
{
if(i>1) cout<<' ';
cout<<color[col][cas[i]];
}
cout<<endl;
}
else cout<<-1<<endl;
}