话不多说,看题解:
A. Windblume Ode(CF1583A)**
**分析:**题意大概是:给一个序列,找一个最大的子序列,使序列的和为合数。首先应该考虑的是原序列本身,若该序列的和为合数则答案显然就是原序列。若和为质数,则和必为奇数(因为n>=3):
注意a是互不相同的,若n个数全为偶数,则合也应该是偶数,矛盾。
于是n个数中有一个奇数,去掉该奇数剩余的数是偶数而且大于2(因为n>=3),因此必存在一个长为n-1的子序列和为合数。
代码:
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int a[220];
bool check(int x){
for(int i=2;i*i<=x;i++){
if(x%i==0) return 0;
}
return 1;
}
void solve(){
int n;cin>>n;
int s=0;
for(int i=1;i<=n;i++){
cin>>a[i];
s+=a[i];
}
if(!check(s))
{
cout<<n<<endl;
for(int i=1;i<=n;i++) cout<<i<<" \n"[i==n];
}
else
{
cout<<n-1<<endl;
for(int i=1;i<=n;i++){
if(a[i]%2)
{
for(int j=1;j<=n;j++)
if(j!=i)cout<<j<<" ";
cout<<endl;
return;
}
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){
solve();
}
return 0;
}
B. Omkar and Heavenly Tree(CF1583B)
分析:题意:给出一些树上的限制条件,点B不在点A和点C的最短路上,让你构造一颗符合条件的树。
这里考查了一种很常见的图(星图)
由于限制数不超过n-1,因此至少有一个点是没有“不在路径上”这个限制的,找出一个这种点,以他为中心建一个星图就好了。
代码:
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int n,m;
int vis[100010];
void solve(){
int a,b,c;
cin>>n>>m;
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++){
cin>>a>>b>>c;
vis[b]=1;
}
int ans;
for(int i=1;i<=n;i++){
if(!vis[i])
{
ans=i;
break;
}
}
for(int i=1;i<=n;i++){
if(i!=ans)
{
cout<<ans<<" "<<i<<endl;
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){
solve();
}
return 0;
}
C. Omkar and Determination(CF1583C)
分析:有若干次询问,每次询问的意思是:
将x1~x2列之间的列取出来,得到一个新的图。新的图哪些点可以走出去是很容易求的。若只告诉你哪些点可以走出去,你能将这个新图换原出来吗?(注意每次询问是独立的,不会改变原图)
很显然,在新图中,如果一个点的上面和左面都是X,那么这个点是走不出去的,无论它本身是不是X,于是乎这个点就变得不确定了,这个点不确定之后这一列就不确定了,这一列不确定那么整个子图就不确定了。反之,没如果这样的点,那么每个点就都能根据它左边和上边的点来确定自己的状态,于是所有的点都确定了。于是问题变成了,x1~x2(不包括x1,因为x1是左边界,它的左边没有叉)中有没有某一列含有不确定点?
我们可以预处理出所有不确定点,进而得到不确定列,然后用前缀和回答询问。
代码:
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
vector<vector<int> >mp1,mp2;
int a[1000010],s[1000010];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n,m;
cin>>n>>m;
mp1.resize(n+5);
mp2.resize(n+5);
char ch;
for(int i=1;i<=n;i++){
mp1[i].resize(m+5);
mp2[i].resize(m+5);
for(int j=1;j<=m;j++){
cin>>ch;
mp1[i][j]=ch=='.'?0:1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
mp2[i][j]=0;
if(i!=1&&j!=1)
{
if(mp1[i-1][j]==1&&mp1[i][j-1]==1)
{
mp2[i][j]=1;
a[j]=1;
}
}
}
}
for(int i=1;i<=m;i++){
s[i]=s[i-1]+a[i];
}
int q;cin>>q;
while(q--){
int l,r;cin>>l>>r;
if(s[r]-s[l]) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
return 0;
}
D. Omkar and the Meaning of Life(CF1583D)
**分析:**理解题意不难,我直接说思路和解法吧:
可以询问2n次,我们可以考虑对 n 位每位询问两次。一种比较简单的询问数列是1 1 1 1 2 1 1 1 ,也就是第 i 位是2,其他的都是1。这有什么用呢?如果得到的 s 序列有两个数相同,那么有一个一定是第 i 位,这相当于第 i 位加 1以后得到了另一位。如果 k!=i,那么这一位就是 k。因此,通过这种询问我们可以得到某一位的后一位是谁,从对称的角度我们也能得到每一位的前一位是谁,这样答案就出来了。
代码:
#include<bits/stdc++.h>
using namespace std;
int nxt[110],pre[110],a[110];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n;cin>>n;
for(int i=n;i>=1;i--){
int g;
cout<<"? ";
for(int j=1;j<=n;j++){
if(j==i) cout<<1;
else cout<<2;
if(j!=n) cout<<" ";
}
cout<<endl;
cout.flush();
cin>>g;
if(g!=0&&g!=i)
{
pre[i]=g;
nxt[g]=i;
}
cout<<"? ";
for(int j=1;j<=n;j++){
if(j==i) cout<<2;
else cout<<1;
if(j!=n) cout<<" ";
}
cout<<endl;
cout.flush();
cin>>g;
if(g!=0&&g!=i)
{
nxt[i]=g;
pre[g]=i;
}
}
int ans;
for(int i=1;i<=n;i++)
{
if(pre[i]==0)
{
ans=i;
break;
}
}
cout<<"! ";
for(int i=1;i<=n;i++)
{
a[ans]=i;
ans=nxt[ans];
}
for(int i=1;i<=n;i++)
{
cout<<a[i];
if(i!=n) cout<<" ";
}
cout<<endl;
cout.flush();
return 0;
}