Codeforces 1200-1400
1513B - AND Sequences
题意:t组测试每组测试给定一个长度为n的数组a[ ] 问在全排列的数组中能找出几种使得or all i from 1 to n 使得a1&a2&…&ai=ai+1&ai+2&…&an
思路:将所有的数字与一遍得出x如果存在这样的排列那么至少会存在两个数字等于x (记数组中等于x的数的个数为sum )那么根据组合可得(sum-1)sum(n-2)!
代码如下
#include<iostream>
#define ll long long
using namespace std;
const int N=2e5+10,mod=1e9+7;
ll a[N];
ll fac(ll x)
{
ll ans=1;
for(ll i=1;i<=x;i++) ans=ans*i%mod;
return ans%mod;
}
int main()
{
int t;cin>>t;
while(t--)
{
ll n;cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
ll x=a[0],sum=0;//sum为数组中等于x的个数
for(int i=1;i<n;i++) x=x&a[i]; //x为全部数字与后的结果
for(int i=0;i<n;i++)
if(a[i]==x)
sum++;
//cout<<sum<<' '<<n<<endl;
if(sum<2) cout<<0<<endl; //如果sum<2那么不存在排列情况
else cout<<(sum-1)*sum%mod*fac(n-2)%mod<<endl;
}
return 0;
}
1510K - King’s Task
题意:给你一段含全排列n*2的数列 问你在一下两种操作中是否有可能使得他们从小到大有序
1.交换奇数位与后面的偶数位
2.交换第i位和n+i位
思路:题目给了三秒的时间n是2000 所以可以暴力
1.一二两种操作不可能重复操作因为重复操作两次就会复原(懂得都懂)
2.循环运行一二两个操作如果在某次运行后数组回到了一开始的状态那么说明他永远也不可能有序了直接输出-1
3.那么是先运行1操作呢还是先运行2操作呢 我的想法是先交替运行1 2操作当回到一开始的状态时说明是不可能有序的直接输出-1否则说明他肯定是可以有序的。那么我们在交替运行2 1操作 记录下这两种方法的次数比较取小即可
代码如下:
#include<iostream>
using namespace std;
const int N=2e3+10;
int a[N],b[N],c[N];
int n;
int ans1,ans2,ans;
bool flag1,flag2;
bool check(int a[])//判断是否回到了一开始的状态
{
for(int i=1;i<=2*n;i++)
if(a[i]!=b[i])
return false;
return true;
}
bool check2(int a[])//判断是否已经有序了
{
for(int i=1;i<=2*n;i++)
if(a[i]!=i)
return false;
return true;
}
void operate1(int a[])//操作一
{
for(int i=1;i<2*n;i+=2) swap(a[i],a[i+1]);
}
void operate2(int a[])//操作二
{
for(int i=1;i<=n;i++) swap(a[i],a[i+n]);
}
int main()
{
cin>>n;
for(int i=1;i<=2*n;i++)
{
cin>>a[i];
b[i]=a[i];
c[i]=a[i];
}
if(check2(a))//如果已经有序了直接输出0
{
cout<<0<<endl;
return 0;
}
while(1)// 1 2操作
{
operate1(a);
ans1++;
if(check(a))
{
flag1=true;
break;
}
if(check2(a)) break;
operate2(a);
ans1++;
if(check(a))
{
flag1=true;
break;
}
if(check2(a)) break;
}
if(flag1)
{
cout<<-1<<endl;
return 0;
}
while(1) //2 1 操作
{
operate2(c);
ans2++;
if(check(c))
{
flag2=true;
break;
}
if(check2(c)) break;
operate1(c);
ans2++;
if(check(c))
{
flag2=true;
break;
}
if(check2(c)) break;
}
//cout<<ans1<<' '<<ans2<<endl;
ans=min(ans1,ans2);// 两种方法取小
cout<<ans<<endl;
return 0;
}
D - Epic Transformation
题意:给定一个数列每次取两个不相同的数字删除 问你最后最小的数组长度是多少
思路:先将数组从小到大排列 然后双指针 i指向数组中间 j指向数组的最后位置 如果指向的两个数字相同那么i向前移否则i与j都向前移 当i<0||j<数组中间位置时推出 结果就是数组的长度减去i与j都向前移的情况
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int a[N];
int n;
int main()
{
int t;cin>>t;
while(t--)
{
int ans=0;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
int i,j;
if(n%2) i=n/2,j=n-1;
else i=n/2-1,j=n-1;
int x=i;
while(i>=0&&j>x)
{
if(a[i]==a[j])
i--;
else
{
ans++;
i--;
j--;
}
}
cout<<n-ans*2<<endl;
}
return 0;
}
C - Fibonacci Words(愚人节)
题意:斐波那契字符 与菲波那切数列差不多 问你这串字符串符不符合斐波那契
思路:将A-Z分别映射成0-25 就是玩
代码如下:
#include<iostream>
using namespace std;
int a[100];
string s;
int main()
{
cin>>s;
for(int i=0;i<s.size();i++) a[i]=s[i]-'A';
if(s.size()<=2)
{
cout<<"YES"<<endl;
return 0;
}
for(int i=2;i<s.size();i++)
if(a[i]!=(a[i-1]+a[i-2])%26)
{
//cout<<a[i]+'A'<<endl;
cout<<"NO"<<endl;
return 0;
}
cout<<"YES"<<endl;
return 0;
}
1498B - Box Fitting
题意:给了n个知道长度和高度都为一的矩形和一个高度无限和已知长度为m的容器 把这些矩形全部装进这个容器中能装的最小高度是多少
思路:从大到小排序一遍 然后开一个优先队列 如果当前矩形的长度能放进队列中的话放进去 否则新开一个队列放进去 就是玩
代码如下:
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N];
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int t;cin>>t;
while(t--)
{
priority_queue<int> p;
int ans=0;
int n,m;cin>>n>>m;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n,cmp);
p.push(0);
for(int i=0;i<n;i++)
{
int l=p.top();
p.pop();
if(a[i]<=l) p.push(l-a[i]);
else
{
ans++;
p.push(l);
p.push(m-a[i]);
}
}
cout<<ans<<endl;
}
return 0;
}
1495A - Diamond Miner
题意:给你n个x轴上的点和n个y轴上的点问你他们两两配对组成的三角形的斜边最小和是多少
思路:可以证明 每次都选最小的点构成斜边这样的做法是最优的(数学可证明 三角形斜边之和小于其他两边之和) 就是玩
tips:这道题会卡输入 建议用scanf或者关闭输入输出同步
代码如下:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+10;
double a[N],b[N];
double xx(double a,double b)
{
return sqrt(pow(a,2)+pow(b,2));
}
int main()
{
double sum=0;
int t;cin>>t;
while(t--)
{
double sum=0.0;
int g=0,h=0;
int n;cin>>n;
for(int i=0;i<2*n;i++)
{
double x,y;scanf("%lf%lf",&x,&y);
if(x==0) b[h++]=abs(y);
else a[g++]=abs(x);
}
sort(a,a+g);
sort(b,b+h);
for(int i=0;i<n;i++) sum+=xx(a[i],b[i]);
printf("%.12lf\n",sum);
}
return 0;
}
1511B - GCD Length(1100)
题意:给定a b c找出长度为a和b的数字使他们的最大公约数是c长度的数字
思路:如果c=min(a,b)那么就搞成1ea 1eb 否则的话就搞成1ea (b-c)个9*1ec 就是玩
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
void solve()
{
int a,b,c;
cin>>a>>b>>c;
if(c==min(a,b))
{
string s1="1",s2="1";
for(int i=1;i<a;i++)
s1+="0";
for(int i=1;i<b;i++)
s2+="0";
cout<<s1<<' '<<s2<<endl;
}
else
{
string s1="1",s2="";
for(int i=1;i<a;i++)
s1+="0";
for(int i=0;i<b-c+1;i++)
s2+="9";
for(int i=0;i<c-1;i++)
s2+="0";
cout<<s1<<' '<<s2<<endl;
}
}
int main()
{
int t;cin>>t;
while(t--)
solve();
return 0;
}
1494B - Berland Crossword
题意:给你一个n*n的二维格子在给定四条边上要放的小格子的数量 问你有没有可能将他给的小格子放进这个二维的格子里
思路:既然他问有没有可能那么我们就要采取最优的方法 二维格子的四个角是我们优先考虑的因为一个角我们可以去掉两个小格子 然后我们就依次遍历四个角最后每条边剩下的格子如果全部都小于等于n-2的话那么就是可行的(需要注意的是依次遍历的顺序是一定的 但是从哪个开始是不一定的所以每种情况都要遍历 如果有一种成功那么就是可行的)
代码如下:
#include<iostream>
using namespace std;
int n;
bool check(int a,int b,int c,int d)
{
if(a&&b) a--,b--;
if(b&&c) b--,c--;
if(c&&d) c--,d--;
if(a&&d) a--,d--;
if(a<=n-2&&b<=n-2&&c<=n-2&&d<=n-2) return true;
return false;
}
int main()
{
int t;cin>>t;
while(t--)
{
int a,b,c,d;
cin>>n>>a>>b>>c>>d;
if(check(a,b,c,d)||check(b,c,d,a)||check(c,d,a,b)||check(d,a,b,c)) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
1493B - Planet Lapituletti
题意:给你一个时间进制和一个时间要求输出离这个事件最近的镜像时间(纯模拟) 需要注意的是你的镜像时间不能超过给的时间进制
思路:没啥思路模拟呗
代码如下:
#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ll long long
#define int long long
#define pii pair<int, int>
#define lowbit(x) (x &(-x))
#define ls(x) x<<1
#define rs(x) (x<<1+1)
#define me(ar) memset(ar, 0, sizeof ar)
#define mem(ar,num) memset(ar, num, sizeof ar)
#define rp(i, n) for(int i = 0, i < n; i ++)
#define rep(i, a, n) for(int i = a; i <= n; i ++)
#define pre(i, n, a) for(int i = n; i >= a; i --)
#define IOS ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
const int way[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
using namespace std;
const int inf = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
const ll mod = 1e9+7;
const int N = 2e5 + 5;
inline void read(int &x){
char t=getchar();
while(!isdigit(t)) t=getchar();
for(x=t^48,t=getchar();isdigit(t);t=getchar()) x=x*10+(t^48);
}
int t, h, m, x, y;
bool ok(int x){
if(x<10){
if(x==0||x==1||x==2||x==5||x==8) return 1;
else return 0;
}
else if(x==100) return 1;
else{
int a = x/10, b = x%10, flag = 0;
if(a==0||a==1||a==2||a==5||a==8) flag = 1;
if((b==0||b==1||b==2||b==5||b==8)&&flag) return 1;
else return 0;
}
}
int fi(int x){
if(x==0||x==1||x==8) return x; //得到对应数字
if(x==2) return 5;
if(x==5) return 2;
}
bool check(int a, int b){
if(!ok(a)||!ok(b)) return 0; //如果不可翻转
int flag = 0;
if(b==100) x = 1;
else x = fi(b%10)*10+fi(b/10);
if(a==100) y = 1;
else y = fi(a%10)*10+fi(a/10);
// cout << "a: " << a << " b: " << b << endl;
// cout << "x: " << x << " y: " << y << endl;
if(x<h&&y<m){
if(a<10) cout << 0 << a << ":";
else cout << a%h << ":";
if(b<10) cout << 0 << b <<endl;
else cout << b%m << endl;
return 1;
}
return 0;
}
signed main()
{
IOS;
cin >> t;
while(t --){
cin >> h >> m;
int a, b; char c;
cin >> a >> c >> b;
for(; ; b ++){
if(b&&b%m==0) a ++;
// cout << "check: " << check(a%h, b%m) << endl;
if(check(a%h, b%m)) break;
}
}
return 0;
}
1491B - Minimal Cost
题意:二维矩阵中每行都放一个障碍物 障碍物上下移动花费u元左右移动花费v元 问到达二维矩阵的右下角最少需要花费多少钱
思路:我们可以把障碍物的情况分为三种 1.障碍物没有构成一道墙我们可以直接过去不需要花钱 2.障碍物构成了一道不完美的墙花费u和v的最小值就可以过去 3.障碍物构成了一道竖立的墙这时我们就要先把某个障碍物向右移动在向右或上下移动 花费最小的钱是u+min(v,u)
代码如下:
#include<iostream>
#include<cmath>
using namespace std;
const int N=110;
int a[N];
int n,v,u;
bool check1(int a[])
{
for(int i=1;i<n;i++)
if(abs(a[i]-a[i-1])>=2)
return true;
return false;
}
bool check2(int a[])
{
for(int i=1;i<n;i++)
if(a[i]!=a[i-1])
return true;
return false;
}
int main()
{
int t;cin>>t;
while(t--)
{
cin>>n>>v>>u;
for(int i=0;i<n;i++) cin>>a[i];
if(check1(a)) cout<<0<<endl;
else if(check2(a)) cout<<min(v,u)<<endl;
else cout<<u+min(v,u)<<endl;
}
return 0;
}
1491B - Minimal Cost
题意:给定了一串数字规定大的或者相等的可以吃掉小的或者相等的然后他可以变强继续吃掉其他人如果他吃掉了其他所有人那么他就是冠军 问你这一串数字里面有多少可以成为冠军的(注意每个数字都很大吃掉其他人的时候要开个 long long)
思路:先排序一下 这个肯定是可以二分的 当小的可以成为冠军那么大的肯定是可以成为冠军的 所以二分去搜索 多的不说了看代码吧
代码如下:
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=2e5+10;
ll a[N],b[N];
bool vis[N];
ll n;
ll sum;
bool check(ll x)
{
//bool flag=false;
for(int i=0;i<n;i++)
if(x>=a[i]&&!vis[i])
x+=a[i];
if(x==sum) return true;
return false;
}
int main()
{
int t;cin>>t;
while(t--)
{
sum=0;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
b[i]=a[i];
sum+=a[i];
}
sort(a,a+n);
ll l=0,r=n-1;
while(l<r)
{
ll mid=l+r>>1;
vis[mid]=true;
if(check(a[mid])) r=mid;
else l=mid+1;
vis[mid]=false;
}
ll x=a[l],ans=0;
for(int i=0;i<n;i++)
if(a[i]>=x)
ans++;
cout<<ans<<endl;
for(int i=0;i<n;i++)
if(b[i]>=x)
cout<<i+1<<' ';
cout<<endl;
}
return 0;
}