A. Crossmarket
题目链接:Problem - A - Codeforces
样例输入:
7
7 5
5 7
1 1
100000 100000
57 228
1 5
5 1
样例输出:
15
15
0
299998
340
5
5
题意:给定一个n*m的小方格,有两个人,一个人位于左上角,要去右下角,另一个人位于右下角要去左上角,一个人每走一步会消耗1个能量,但是这个人走过的地方会留下轨迹,如果另一个人走到了这个人走过的轨迹上,则可以选择耗费1能量使得这个人传送到另一个人已经走过的的任意地方,问两个人最少耗费的能量是多少?
分析:最简单的方式就是一个人先经过另一个人走到终点,然后另一个人再通过已经走过的路径传送一条边然后再走向终点,这样的花费一般来说是最小的,也有那种不传送的情况,比如起点终点重合就不需要传送,这个简单分析一下就好,下面是代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e5+10;
int main()
{
int T;
cin>>T;
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
printf("%d\n",n+m-2+min(1,max(n-1,m-1))+min(n-1,m-1));
}
return 0;
}
B. Beautiful Array
题目链接:Problem - B - Codeforces
样例输入:
8
1 6 3 100
3 6 3 12
3 6 3 19
5 4 7 38
5 4 7 80
99978 1000000000 100000000 1000000000000000000
1 1 0 0
4 1000000000 1000000000 1000000000000000000
样例输出:
-1
-1
0 0 19
0 3 3 3 29
-1
-1
0
0 0 0 1000000000000000000
题意:给定一个长度为n的数组a,定义数组a的值就是,现在告诉我们数组的长度,以及k和数组a的值还有数组a中所有的元素和s,让我们构造一个满足上述定义的数组a。不能构造则输出-1.
分析:先来看一下什么情况是构造不出来的情况,首先如果要是s
细节见代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e5+10;
int main()
{
int T;
cin>>T;
while(T--)
{
long long n,k,b,s;
scanf("%lld%lld%lld%lld",&n,&k,&b,&s);
if(s(k*(b+1)-1+(k-1)*(n-1))) puts("-1");
else
{
long long t=min(s,k*(b+1)-1);
printf("%lld ",t);
s-=t;
for(int i=2;i<=n;i++)
if(s>=k-1) printf("%lld ",k-1),s-=k-1;
else if(s) printf("%lld ",s),s=0;
else printf("0 ");
puts("");
}
}
return 0;
}
C. Monoblock
题目链接:Problem - C - Codeforces
样例输入:
5 5
1 2 3 4 5
3 2
4 2
3 1
2 1
2 2
样例输出:
29
23
35
25
35
题意:给定一个长度为n的数组,我们定义一个区间的值为相等连续段的个数,求这个数组所有区间的值的和,此外我们会对这个数组进行m次操作,每次操作将一个元素值修改为另一个元素值,每次修改后都需要输出这个数组所有区间的值的加和。
分析:还是考虑每个数的贡献,为了防止重复计数,我们把一个区间内连续一段数所产生的贡献1看作是这段数中最左边的那个数产生的,比如3334433,这个区间值为3,其中1个是由最左边的那个3贡献的,另一个是最左边的4贡献的,还有一个是最右边一段连续的3中最左边的那个3贡献的,明白了这些定义之后我们来看一下如何计算一个数在整个数组中的贡献。
以1 2 3 3 4 5,我们来看一下这两个3的贡献,首先来看一下第一个3的贡献,这个3所贡献的区间的左边界可以是1,2,3,右边界可以是3,4,5,6,所以总的贡献区间数就是3*4=12,下面我们来看一下第二个3的贡献,第二个3的贡献区间的左边界可以取哪些值呢?可以取3么,不可以,因为这个区间中连续3的贡献是第一个3的,所以他的左边界只能是4,而右边界可以是任何值,也就是1*3=3,这样我们就能不重不漏地计算所有数的贡献。但是需要注意的一点是我们每次修改一个数后对他旁边的两个数的贡献也会产生影响,所以我们每次需要考虑这个数及其相邻的两个数。
细节见代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e5+10;
int a[N],n;
long long cal(int pos)
{
if(pos<1||pos>n) return 0;
int l;
if(pos==1) l=1;
else
{
if(a[pos]==a[pos-1]) l=1;
else l=pos;
}
return 1ll*l*(n-pos+1);
}
int main()
{
int m;
cin>>n>>m;
long long ans=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
ans+=cal(i);
while(m--)
{
int pos,val;
scanf("%d%d",&pos,&val);
ans-=cal(pos-1);
ans-=cal(pos);
ans-=cal(pos+1);
a[pos]=val;
ans+=cal(pos-1);
ans+=cal(pos);
ans+=cal(pos+1);
printf("%lld\n",ans);
}
return 0;
}
D. 2+ doors
题意链接:Problem - D - Codeforces
样例输入:
4 3
1 2 3
1 3 2
4 1 2
样例输出:
0 3 2 2
题意:n个数,m个限制,每次限制给定i,j,x代表a[i]&a[j]=x,求最小的满足所有限制的字典序。
分析:我们按照位进行分析,位跟位之间是不会产生影响的,我们以某一位来进行分析。
如果两个数与值在第i位为0,那么在两者之间就连一条权值为0的边,否则连一条权值为1的边,我们首先处理权值为0的边,权值为0的边代表这一位上两个数均为0,然后我们就已经确定了这一位上一些数的取值,然后我们就遍历一边权值为1的边,如果某一个数已经确定是0,那么另一个数一定是1,因为两者至少有一个是1,最后我们整体处理那些权值为1的边,这些边按照字典序进行排序,先处理字典序小的,如果当前位还没确定就优先取0,然后把与该点有关的点全部取1,如果当前位已经确定,说明当前位一定是1,因为我们不可能确定一个数一定取0只能确定一个数一定取1,如果当前位取1,那么与当前点相关的点的取值我们可以不作限制,可以取0也可以取1.就是按照这样的贪心策略进行构造。细节见代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e6+10;
int a[N];
int u[N],v[N],z[N];
bool vis[N];
vector >p;
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&u[i],&v[i],&z[i]);
for(int i=0;i<30;i++)
{
p.clear();
for(int j=1;j<=n;j++) vis[j]=false;
for(int j=1;j<=m;j++)
{
if(u[j]>v[j]) swap(u[j],v[j]);
if(z[j]>>i&1)
p.push_back({u[j],v[j]});
else
vis[u[j]]=vis[v[j]]=true;
}
sort(p.begin(),p.end());
for(int j=0;j>i&1)==0)&&(!vis[y])) vis[y]=true,a[y]+=1<>i&1)==0)&&(!vis[x])) vis[x]=true,a[x]+=1<>i&1)//当前这个数填的是1
continue;//后面这个数可以随便填
else
a[y]+=1<