[题目链接:]http://codeforces.com/group/NVaJtLaLjS/contest/236618
A. QAQ(签到题)
题意: 略
思路: 略
B. Mike and Shortcuts(最短路)
题意: 从1–n路口之间有n-1条路相连,另外每个路口也许还有一条捷径(单向)通往另外一个路口,问到达每个路口需要花费多少时间
思路: 建立一个图,n-1条双向边,然后捷径视为单向边,求最短路
#include<bits/stdc++.h>
using namespace std;
int n,a[200005],dis[200005],head[200005],vis[200005],cnt;
struct node
{
int v,next;
}edge[600005];
void add(int u,int v)
{
edge[++cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt;
}
void bfs()
{
vis[1]=1;
queue<int>que;
que.push(1);
while(que.size())
{
int u=que.front();
que.pop();
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(!vis[v])
{
dis[v]=dis[u]+1;
vis[v]=1;
que.push(v);
}
}
}
}
int main()
{
memset(head,-1,sizeof head);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<n;i++)
{
add(i,i+1);
add(i+1,i);
}
for(int i=1;i<=n;i++)
if(i!=a[i])
add(i,a[i]);
bfs();
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
return 0;
}
C. Merge Sort(思维+模拟)
题意: 问一个含1-n的序列经过k次归并排序后是一个有序的序列,求原序列,若分块后的某段序列已经有序,则该段序列结束。
思路: 因为归并排序如同一颗满二叉树有奇数个结点,所以k若为偶数则不成立,先排好1-n,每次交换左分块右边界与右分块的左边界,然后push进队列,每次归并次序+2,直到归并次序为k结束,若是队列为空时小于k,则不满足
注意归并排序(分块的区间均为【l,mid)【mid,r)左闭右开)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N];
int main()
{
int n,k;
cin>>n>>k;
if(!(k%2))
{
cout<<"-1"<<endl;
return 0;
}
for(int i=0;i<n;i++)
{
a[i]=i+1;
}
int ans=1;
queue<pair<int ,int> >que;
que.push(make_pair(0,n));
while(que.size())
{
if(ans==k)
break;
int l=que.front().first,r=que.front().second;
que.pop();
if(l+1==r)
continue;
int mid=(l+r)/2;
swap(a[mid],a[mid-1]);
ans+=2;
que.push(make_pair(l,mid));
que.push(make_pair(mid,r));
}
if(ans<k)
cout<<"-1"<<endl;
else
{
for(int i=0;i<n;i++)
cout<<a[i]<<" ";
}
return 0;
}
D. Roads not only in Berland(并查集)
题意: 有N个城市,共有N-1条双向路,现在问最少取消多少条路,并重新建立新路使得每座城市都可以到达任何一个城市
思路: 并查集求出多少个联通块,在成环的取消一条路,使环断开,连接其他联通块,若求得m个联通块,则需要新建立m-1条新路
#include<bits/stdc++.h>
using namespace std;
int cnt,a[1005],b[1005];
struct node
{
int x,y;
}c[1005];
int fid(int x)
{
int r=x;
while(a[r]!=r)
r=a[r];
return r;
}
void edit(int x,int y)
{
int fx=fid(x);
int fy=fid(y);
if(fx==fy)
{
c[++cnt].x=x;
c[cnt].y=y;
}
else
a[fx]=fy;
}
int main()
{
int n,x,y;
cin>>n;
for(int i=1;i<=n;i++)
a[i]=i;
for(int i=1;i<n;i++)
{
cin>>x>>y;
edit(x,y);
}
cnt=0;
for(int i=1;i<=n;i++)
{
if(a[i]==i)
b[++cnt]=i;
}
cout<<cnt-1<<endl;
for(int i=1;i<cnt;i++)
{
cout<<c[i].x<<" "<<c[i].y<<" "<<b[i]<<" "<<b[i+1]<<endl;
}
return 0;
}
E. New Year Book Reading(思维+贪心)
题意: 有N本书,以及M天的看书顺序,且书从上到下摞成一叠,问这些书如何叠使得看书所需搬动书最优,也就是搬动书的总质量最小
思路: 按照每本第一次出现的顺序从上往下叠,这样搬动的书的质量最小,就这样从第一天开始贪心
#include<bits/stdc++.h>
using namespace std;
int w[505],book[1005],vis[505],shelf[505];
int main()
{
int n,m,top=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>w[i];
for(int i=1;i<=m;i++)
{
cin>>book[i];
if(!vis[book[i]])
{
vis[book[i]]=1;
shelf[++top]=book[i];
}
}
int ans=0;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=top;j++)
{
if(shelf[j]==book[i]){
for(int k=j;k>1;k--)
{
shelf[k]=shelf[k-1];
}
shelf[1]=book[i];
break;
}
ans+=w[shelf[j]];
}
}
cout<<ans<<endl;
return 0;
}
或者直接用vetor,G.erase(G.begin()+j);表示删除G[ j ]元素,G[ j ]后面的元素往前进一位
G.insert(G.begin(),book[i]); 在G[ 0 ]插入一个元素,原本的元素往后退一位
#include<bits/stdc++.h>
using namespace std;
int w[505],book[1005],vis[505];
vector<int>G;
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>w[i];
for(int i=1;i<=m;i++)
{
cin>>book[i];
if(!vis[book[i]])
{
vis[book[i]]=1;
G.push_back(book[i]);
}
}
int ans=0;
for(int i=1;i<=m;i++)
{
for(int j=0;j<G.size();j++)
{
if(G[j]==book[i]){
G.erase(G.begin()+j);
G.insert(G.begin(),book[i]);
break;
}
ans+=w[G[j]];
}
}
cout<<ans<<endl;
return 0;
}
F. Flipping Game(签到题)
题意: 一个序列只含有1或0 ,一区间内的数被翻转,则0变为1,1变为0,问翻转一次使得这个序列总和最大,则最大为多少
思路: 由于题目数据较小,直接枚举每个可能的区间就行,复杂度O(n^2)
#include <bits/stdc++.h>
using namespace std;
const int Maxn=105;
int num[Maxn],sum[Maxn];
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
sum[i]=sum[i-1]+num[i];
}
int ans=0;
for (int i=1;i<=n;i++)
{
for (int j=i;j<=n;j++)
{
int temp=sum[i-1]+sum[n]-sum[j]+(j-i+1)-(sum[j]-sum[i-1]);
ans=max(ans,temp);
}
}
printf("%d\n",ans);
return 0;
}
G. Nephren gives a riddle(思维+搜索)
题意: 已知第0句话和第一句话,现给你q组数据,每一组有两个数,一个是第n句话,一个是这个第n句话中的第k个字母,求出这个字母。若不存在第k个字母则输出‘ . ’;
思路: 就是无论是第几句话,都分成五个部分,第一个就是双引号前面的,第二个是双引号中间的,第三个是第一个双引号后面第二个双引号前面的,第四个是第二个双引号,第五个是第二个双引号后面的。根据k判断它在这五个部分中的哪一个,找到后就进行递归搜索
需要注意的是题目限制k<=1e18,在第53句话时就超出1e18了,所以不必需要知道后面的字母,防止字母数爆long long 而出错
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
char f1[]="What are you doing at the end of the world? Are you busy? Will you save us?";
char f2[]="What are you doing while sending \"\"? Are you busy? Will you send \"\"?";
ll len1;
ll len[N];
char tmp[10];
void init()
{
len[0]=strlen(f1);
len1=strlen(f2);
for(int i=1;i<=N;i++)
{
if(len[i-1]>1e18){
len[i]=len[i-1];
}
else
len[i]=len[i-1]*2+len1;
}
}
char dfs(int n,ll k)
{
if(n==0)
{
if(k<len[0])
return f1[k];
return '.';
}
if(k<34)
return f2[k];
k-=34;
if(k<len[n-1])
return dfs(n-1,k);
k-=len[n-1];
if(k<32)
return f2[k+34];
k-=32;
if(k<len[n-1])
return dfs(n-1,k);
k-=len[n-1];
if(k<2)
return f2[k+66];
return '.';
}
int main()
{
ll k;
init();
int n,q;
//cout<<len[53]<<endl<<len1;
cin>>q;
for(int i=1;i<=q;i++)
{
cin>>n>>k;
k--;
tmp[i]=dfs(n,k);
}
for(int i=1;i<=q;i++)
cout<<tmp[i];
return 0;
}
H. Roma and Changing Signs(贪心)
题意: 给出一个序列该序列有正有负,还有可能为0,有k次修改机会,每次修改一个数就是给那个*-1,问经过k次修改后序列最大的和为多少
思路: 这道有个坑点就是一个数可以被变换无数次,而且这k次必须用完,所以我们可以先进行排序,若k小于等于负的个数,取前k个最小的负数乘-1,反之k大于负的个数且序列包含0,则所有负数乘于-1,若序列不含0,看k减去负数的个数后是奇数还是偶数,若是偶数则无影响,若是奇数距离0最近的那个数最终为负数
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxd = 1e5+10;
int arr[maxd],n,k,ans,del,flag=1e9;
int main()
{
// freopen("a.in","r",stdin);
// freopen("k.out","w",stdout);
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&arr[i]);
if(arr[i]<=0) del++;
flag = min(abs(arr[i]),flag);
}
if(del > k)
{
for(int i=1;i<=k;i++) ans += -arr[i];
for(int i=k+1;i<=n;i++) ans += arr[i];
}
else
{
for(int i=1;i<=del;i++) ans += -arr[i];
for(int i=del+1;i<=n;i++) ans += arr[i];
ans -= ((k-del)&1)?2*flag:0;
}
printf("%d",ans);
return 0;
}
I. Lucky Tickets(签到题)
题意: 略
思路: 略
J. Steps(模拟)
题意: 给出地图的大小与起点,现有k次移动的规律,每次按照该次移动增量移动,直到无法移动,问总共移动几步
思路: 注意一些变量初始化问题就行了
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int main()
{
ll n,m;
cin>>n>>m;
ll sx,sy;
cin>>sx>>sy;
ll k,dx,dy;
cin>>k;
ll nx,ny,ans=0;
while(k--)
{
nx=INF,ny=INF;
cin>>dx>>dy;
if(dy==0&&dx==0)continue;
if(dx!=0)
{
if(dx>0)
{
nx=(n-sx)/dx;
}
else if(dx<0){
nx=(sx-1)/(-dx);
}
}
if(dy!=0)
{
if(dy>0)
{
ny=(m-sy)/dy;
}
else if(dy<0){
ny=(sy-1)/(-dy);
}
}
ll minn=min(ny,nx);
if(minn==INF)
continue;
sy+=minn*dy;
sx+=minn*dx;
//cout<<sx<<endl<<sy;
ans+=minn;
}
cout<<ans<<endl;
return 0;
}