A. Windblume Ode
题意是给你一个数组。然后要你求得一个子序列,这个子序列每个数的和是一个合数,并且是该数组能得到的最大的合数。输出这个子序列中每个元素的下标。
一开始看到这个题目的n的要求是大于等于3,又是求合数。想到3个数相加,如果是奇数,那么肯定有一个数是奇数,减去这个奇数就可以得到一个偶数。如果和是偶数,那么一定是合数。
3个数以上也是一样的。所以可以只删去一个数就可以使这个和从素数变成合数。
所以就先求得所有数的和,先判断和是不是合数。如果不是。
然后从小到大遍历一下删去这个数是否是合数即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int inf = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;
int n, m, T;
int flag;
struct node {
int data;
int set;
};
node a[MAXN];
bool cmp(node x, node y)
{
return x.data < y.data;
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> T;
while (T--) {
cin >> n;
int sum = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i].data;
sum += a[i].data;
a[i].set=i;
}
flag = 0;
for (int i = 2; i < sum; i++) {
if (sum % i==0) {
flag = 1;
break;
}
}
// cout<<sum<<endl'
if (flag) {
cout << n << endl;
for (int i = 1; i <= n; i++) {
cout << i << " ";
}
cout << endl;
} else {
sort(a + 1, a + 1 + n, cmp);
for (int i = 1; i <= n; i++) {
int d = sum - a[i].data;
for (int j = 2; j < d; j++) {
if (d%j==0) {
flag = a[i].set;
// cout<<flag<<endl;
break;
}
}
if(flag) break;
}
cout << n - 1 << endl;
for (int i = 1; i <= n; i++) {
if (i == flag) {
continue;
}
cout << i << " ";
}
cout << endl;
}
}
return 0;
}
B. Omkar and Heavenly Tree
题意:给你n个节点和m个约束。让你构造一个满足m个约束的无向图。
一个约束由 a b c 三个数组成,即不能出现 a连接b 然后b连接c的情况。
因为每一个约束,约束的是中间的节点b两头不能是a 或c。
那么我们可以找到一个点,他没有被限制两边的点。我们就可以让所有的点都连接它即可。
同样是观察数据,题目给出的约束是m个,m<n。
所以至少是有一个点是不会被约束的。所以我们找出这个点即可,然后其他所有点都连接他即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int inf=0x3f3f3f3f;
const int MAXN=1e5+10;
int n,m,T;
int flag;
int vis[MAXN];
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>T;
while(T--){
cin>>n>>m;
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
vis[b]=1;
}
for(int i=1;i<=n;i++){
if(vis[i]==0){
for(int j=1;j<=n;j++){
if(i==j) continue;
cout<<i<<" "<<j<<endl;
}
break;
}
}
}
return 0;
}
C. Omkar and Determination
题意:给你一个n*m的地图。'X’表示不可移动,‘.’表示可以移动,但是只能向上或者向左移动。并且移动方向上不能有不可以移动的点。
有k次查询,每次查询是一个区间(l,r)。会将这个区间的列单出拿出来,然后得到这个区间每个点是否可以移动出这个范围的图。无论是从上还是从左都可以。你要输出,是否可以根据这个每个点是否可以移动出这个范围的图,得到原来这个区间的可移动和不可移动的点。
所以题目就变成了,找出这个区间是否有点无论是否是可以移动的点,都被限制了不能移动。因为如果存在这样的点,那么你得到的图中,就无法得到这个点是可移动的还是不可移动的,因为无论是哪个他都移动不出这个范围。
满足这样的点的条件是,上面和左边都是不可以移动的点。这样他就移动不了了,
所以就遍历一下得到这些点即可。
因为每次区间查询都是得到区间的每一列。所以你就只要存每列得到多少个这样的点即可。
因为是区间查询,所以用前缀和来查询这个区间有没有这样的点即可。
然后问题就来了,就是第i列的点满足无法确定的条件之一是,这个点左边是不可以移动的点。
所以如果这个点要是无法确定,必须左边的这一列也要在区间里面。
所以求前缀和的时候,是
vis[r-1]-vis[l-1]
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int inf=0x3f3f3f3f;
const int MAXN=1e6+10;
int n,m,T;
int flag;
char s[2][MAXN];
int vis[MAXN];
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
cin>>s[0];
for(int i=1;i<n;i++){
cin>>s[1];
for(int j=1;j<m;j++){
if(s[0][j]=='X'){
if(s[1][j-1]=='X'){
vis[j]++;
}
}
}
strcpy(s[0],s[1]);
}
for(int i=0;i<m;i++){
vis[i]+=vis[i-1];
}
cin>>T;
while(T--){
int l,r;
cin>>l>>r;
if(l==r){
cout<<"YES"<<endl;
continue;
}
if(vis[r-1]-vis[l-1]){
cout<<"NO"<<endl;
}else{
cout<<"YES"<<endl;
}
}
return 0;
}
D. Omkar and the Meaning of Life
题意是有一个1到n的序列p。你要用最多2n次查询,得到这个序列p。
每次查询是你输出一个序列a 。然后会得到一个序列 b 。 bi=ai+pi;
然后会返回一个数,这个数是 b序列中出现两次或者两次以上的数的最小的下标。
如果不存在返回0;
因为可以查询2n次,所以其实可以每2次查询找到一个数即可。
一种做法是,每次选一个位置加1,然后其他位置都加2,这样就会使b数组中某个数出现两次,然后就会返回下标。因为,只会返回小的那个下标。如果是返回的是你选的那个位置,那这次查询就没有意义。但如果不是,那么就相当于找到了比这个位置的数大1的数的位置。就可以得到一些数关系。
所以可以再进行一轮查询,选择一个位置加2,然后其他位置都加1,然后返回的下标如果不是你选中的位置,那么就是比你选中的位置小1的数的位置。又可以确定一些数的关系。
因为这两轮查询,一个是找小1的数的位置,一个是找大1的数的位置。所以结合两轮查询,可以找到所有的数的位置对应关系,即比他大1的数的位置。
因为在查询中,如果不存在出现两次或两次以上的数就会返回0,所以nxt[0]存的就是数字1的位置。
因为在某次查询时。找不到比他小1的数的位置,根据
nxt[m]=i;//m为查询到的位置。
就将其存了下来,然后遍历一遍即可。
还有一种做法就是。以最后一个数为基准
第一轮查询是,最后一个位置每次都加1,然后其他位置的数从2加到n。
这样每次都可以得到一个下标的数和最后一个数的差距,然后这轮查询之后,就可以统计出有多少的数比他小,就可以得到最后一个数是多少,然后再进行一轮查询。每个数都加1,最后一个数从2加到n即可得到剩下的数是多少。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int inf = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;
int n, m, T;
int flag;
int a[200];
int ans[200];
int nxt[200];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) {
cout << "? ";
for (int j = 1; j <= n; j++) {
if (j == i) {
cout << "1 ";
} else {
cout << "2 ";
}
}
cout << endl;
fflush(stdout);
cin >> m;
if (m != i) {
nxt[m] = i;
}
cout << "? ";
for (int j = 1; j <= n; j++) {
if (i == j) {
cout << "2 ";
} else {
cout << "1 ";
}
}
cout << endl;
cin >> m;
if(m!=i){
nxt[i]=m;
}
fflush(stdout);
}
cout<<"! ";
int cnt = 0, now = nxt[0];
while(now) ans[now] = ++cnt, now = nxt[now];
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
cout<<endl;
return 0;
}