EDU10,C,D,E,典中典。
思路:硬模拟即可。
#include
using namespace std;
const int N = 2e6 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"
void solve()
{
int h1,h2;
cin>>h1>>h2;
int a,b;
cin>>a>>b;
if(h1+8*a>=h2){
cout<<0<<endl;
return ;
}
int day=1;
int x;
if(a<=b){
cout<<-1<<endl;
return ;
}
h1+=8*a-12*b;
if(h1+a*12>=h2){
cout<<day<<endl;
return ;
}
x=h2-h1-a*12;
cout<<day+(x+(a-b)*12-1)/((a-b)*12)<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
思路:排序后奇偶对调即可,不存在不合法的情况。
#include
using namespace std;
const int N = 2e6 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"
void solve()
{
int n;
cin>>n;
vector<int> a(n+5);
for(int i=1;i<=n;i++) cin>>a[i];
sort(a.begin()+1,a.begin()+1+n);
for(int i=2;i+1<=n;i+=2){
swap(a[i],a[i+1]);
}
int f=1;
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
很经典的一道计算贡献的题目。
对于计算贡献区间的题目,我们一般有两种处理方法:
第一种就是固定左端点,去枚举右端点;
第二种就是考虑对于二进制类型的贡献问题,我们将每个数字进行拆位,考虑该数二进制表示下每一位的贡献。
思路:很显然,因为要考虑所有区间对答案的贡献,所以我们思考,对于每一个左端点,是否能求出其能延申的最远距离,即最大合法右端点,我们对限制条件进行处理,即对于给定数对 [ a , b ] [a,b] [a,b], a a a所在的下标 i i i是无法延申到 b b b所在的下标 j j j的,最后按此维护一下最大后缀即可,因为我们顺着来无法退出第 i i i个数最远能够延申到哪,因为只有第 i + 1 i+1 i+1个数确定了其延申范围,我们才能对 i i i进行更新。(因为不会出现第 i + 1 i+1 i+1能到达 j j j,但 i i i无法到达 j j j的情况)。
#include
using namespace std;
const int N = 2e6 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"
void solve()
{
int n,m;
cin>>n>>m;
vector<int> a(n+5),st(n+5);
for(int i=1;i<=n;i++) cin>>a[i],st[a[i]]=i;
vector<int> l(n+5,n),r(n+5);
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
int xi=st[x],yi=st[y];
if(xi>yi) swap(xi,yi);
l[xi]=min(l[xi],yi-1);
}
l[n+1]=n+1;
for(int i=n-1;i>=1;i--){
l[i]=min(l[i+1],l[i]);
}
ll sum=0;
for(int i=1;i<=n;i++){
ll x=l[i]-i+1;
sum+=x;
}
cout<<sum<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
又是典中典,经典二维偏序问题,对于给定线段 [ l , r ] [l,r] [l,r],我们要找到在 [ l , r ] [l,r] [l,r]范围内的线段数,那么可以将 l , r l,r l,r转化为二维坐标系中的一个点,然后我们对所有点按照x轴进行排序,此题就转化为了,对于给定的点 ( l , r ) (l,r) (l,r),横坐标比他大,但纵坐标比他小的点有多少个,又因为我们是对横坐标进行排序,那么我们限制我们只需要从横坐标最大的点开始,使用一个数据结构去维护,对于每个点,有多少个点的纵坐标自身小即可。每遍历完一个点,我们就将该点放入数据结构中即可。
上述过程,我们很容易想到使用树状数组即可对上述内容进行维护。
注意该题数据范围为1e9,所以需要先进行离散化。
#include
using namespace std;
const int N = 2e6 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"
struct MIT//树状数组
{
ll tr[N];
int lowbit(int x) {
return x & (-x);
}
void add(int u, int v) {
for (int i = u; i < N; i += lowbit(i)) {
tr[i] += v;
}
}
ll query(int x) {
ll res = 0;
for (int i = x; i > 0; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
};
MIT c;
void solve()
{
int n;
cin>>n;
vector<p3> se(n+5);
for(int i=1;i<=n;i++){
int l,r;
cin>>l>>r;
se[i]={l,r,i};//因为要进行离散化,所以需要储存编号
}
sort(se.begin()+1,se.begin()+1+n,[](p3 x,p3 y){
return x[0]>y[0];
});
vector<int> g(n+5);
for(int i=1;i<=n;i++){
auto [x,y,z]=se[i];
g[i]=y;
}
sort(g.begin()+1,g.begin()+1+n);
for(int i=1;i<=n;i++){
int t=lower_bound(g.begin()+1,g.begin()+1+n,se[i][1])-g.begin();//经典离散化操作
se[i][1]=t;//第二维才是我们需要的值
}
vector<int> ans(n+5);
for(int i=1;i<=n;i++){//对每个点进行遍历
auto [x,y,z]=se[i];
int res=c.query(y);
ans[z]=res;
c.add(y,1);
}
for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
还是典中典。
很明显的无向图,要求一条路只能通过一遍,标准的缩点提示。
思路:缩点后看从 a − b a-b a−b的路径上是否存在边权为 1 的边即可。
#include
using namespace std;
const int N = 2e6 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 1e9+7;
const int maxv = 4e6 + 5;
// #define endl "\n"
int n,m;
struct node
{
int a,b,w;
}z[N];
vector<pll> e[N];
vector<pll> e2[N];
vector<vector<int> > vdcc;
int dfsn[N],low[N],tot,b[N];
stack<int> s;
void add(int u,int v,int i)
{
e[u].push_back({v,i});
e[v].push_back({u,i});
}
void tarjan(int x,int f)//tarjan缩点,跑出来所有的边双连通分量
{
dfsn[x]=low[x]=++tot,s.push(x);
for(auto [u,i]: e[x]){
if(!dfsn[u]){
tarjan(u,i);
low[x]=min(low[x],low[u]);
}
else if(f!=i) low[x]=min(low[x],dfsn[u]);
}
if(dfsn[x]==low[x]){
vector<int> c;
while(1){
auto t=s.top();
b[t]=vdcc.size();
c.push_back(t);
s.pop();
if(t==x) break;
}
vdcc.push_back(c);
}
}
int st,ed;
int val[N];
int ans;
void dfs(int x,int f,int ans)//记录ans,
{
if(x==ed){
if(ans){
cout<<"YES"<<endl;
}
else cout<<"NO"<<endl;
return ;
}
for(auto [u,w]: e2[x]){
if(u!=f) dfs(u,x,ans|w|val[u]);
}
}
void solve()
{
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
add(u,v,i);
z[i]={u,v,w};
}
cin>>st>>ed;
for(int i=1;i<=n;i++){
if(!dfsn[i]){
tarjan(i,-1);
}
}
for(int i=1;i<=m;i++){//缩点过程
auto [u,v,w]=z[i];
if(b[u]!=b[v]){
e2[b[u]].push_back({b[v],w});
e2[b[v]].push_back({b[u],w});
}
else{//如果当前点在一个连通分量里面,我们就把当前连通分量和w与一下,因为如果当前权值为1,整个连通分量又可以相互到达,所以该连通分量的权值就为1.
val[b[u]]|=w;
}
}
st=b[st],ed=b[ed];
ans=val[st];
dfs(st,-1,ans);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}