895 DIV3 [A~F]
895 DIV3
A.
void solve()
{
int a,b,c;
cin>>a>>b>>c;
int t=abs(a-b);
double tt=t*1.0/2.0/c;
t=tt;
if(t<tt) t++;
cout<<t<<"\n";
}
B:
void solve()
{
int n;
cin>>n;
int maxa=500;
for(int i=1;i<=n;i++){
int d,s;
cin>>d>>s;
s--;
maxa=min(maxa,d+(s/2));
}
cout<<maxa<<"\n";
}
C:
- 是一个元素且为质数,一定不行。
- 其他情况里面只有最大元素<4不行。找一个偶数输出一半一半即可。
void solve()
{
int a,b;
cin>>a>>b;
if(a==b){
int k=-1;
for(int i=2;i<=sqrt(a);i++){
if(a%i==0){
k=i; break;
}
}
if(k==-1){
cout<<"-1\n"; return ;
}
int num=a/k;
int ans1=num; int ans2=a-ans1;
cout<<ans1<<" "<<ans2<<"\n";
}
else{
if(b<4){
cout<<"-1\n"; return ;
}
if(b%2==1) b--;
cout<<b/2<<" "<<b/2<<"\n";
}
}
D:
- 两者可能有重合的部分。预处理出来各自独立的部分有多少。
- 然后x里面全部放最大的,另外一个全部放最小的。
void solve()
{
int n,x,y;
cin>>n>>x>>y;
int p=x*y/gcd(x,y);
int ans1=n/x;
int ans2=n/y;
int num=n/p;
ans1-=num;
ans2-=num;
int sum=0;
sum+=ans1*(n+n-ans1+1)/2;
sum-=ans2*(1+ans2)/2;
cout<<sum<<"\n";
}
E:
- 很明显的线段树了。
- 如果要求更改,就把整个区间的所有0异或结果和1异或结果换一下。
- 因为涉及区间修改,需要用懒标记。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+6;
int ma1[4*N],ma2[4*N];
int lazy[4*N];
string s;
int a[N];
void pushup(int k){
ma1[k]=ma1[2*k]^ma1[2*k+1];
ma2[k]=ma2[2*k]^ma2[2*k+1];
}
void build(int k,int l,int r){
lazy[k]=0;
if(l==r){
if(s[l]=='1') ma1[k]=a[l],ma2[k]=0;
else ma2[k]=a[l],ma1[k]=0;
return ;
}
int mid=(l+r)>>1;
build(2*k,l,mid);
build(2*k+1,mid+1,r);
ma1[k]=ma1[2*k]^ma1[2*k+1];
ma2[k]=ma2[2*k]^ma2[2*k+1];
}
void pushdown(int k){
swap(ma1[2*k],ma2[2*k]);
swap(ma2[2*k+1],ma1[2*k+1]);
lazy[2*k]^=1;
lazy[2*k+1]^=1;
lazy[k]=0;
}
void change(int k,int l,int r,int x,int y){
if(x<=l && r<=y){
swap(ma1[k],ma2[k]);
lazy[k]^=1;
return ;
}
if(lazy[k]!=0) pushdown(k);
int mid=(l+r)>>1;
if(x<=mid) change(2*k,l,mid,x,y);
if(y>mid) change(2*k+1,mid+1,r,x,y);
pushup(k);
}
void solve()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cin>>s;
s="?"+s;
int q;
cin>>q;
build(1,1,n);
while(q--){
int op; cin>>op;
if(op==1){
int l,r;cin>>l>>r;
change(1,1,n,l,r);
// cout<<ma1[1]<<"\n";
}
else{
int g; cin>>g;
if(g==1) cout<<ma1[1]<<" ";
else cout<<ma2[1]<<" ";
}
}
cout<<"\n";
}
signed main()
{
cin.tie(0); ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
F:
- 本身以为直接按照贪心就能过,结果别有洞天。
- 对于点i,往\(a_i\)建立一条边,每一个点都会指向一个点,如果自己比指向的点在最后输出的前面,自己的价值就会翻倍。
- 如果一个点度为0,说明没有人强制要求在此之前,可以直接输出。
拓扑排序,把所有类似的都处理完之后。会只剩下一些环。 - 为什么会只剩下一些环:
- 可以对样例都画一下看看。
- 因为整张图最多只有n条边,而且每一个点只有一个出度。
- 如何处理剩下的环:
对于一个环,里面一定是不能满足所有的都能翻倍的,至少有一个不能翻倍。
可以发现,对于此题,所成环,一定为n个点,n条边,因此,一旦确定了一个不能翻倍之后,一定可以找到一种输出方式使得其他的都可以翻倍。
所以只需要找到每一个环里面的点的价值最小的最后输出就好了。
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+6;
vector<int>tr[N];
int vis[N],a[N],c[N];
int du[N];
int dfn[N],low[N],cnt;
int st[N];
int num,col[N];
vector<int>g[N];
stack<int>q;
void tarjan(int x){
dfn[x]=low[x]=++cnt;
q.push(x);
st[x]=1;
for(auto v:tr[x]){
if(dfn[v]==0){
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if(st[v]) low[x]=min(low[x],dfn[v]);
}
if(dfn[x]==low[x]){
num++;
while(1){
int t=q.top();
q.pop();
st[t]=0;
col[t]=num;
g[num].push_back(t);
if(t==x) break;
}
}
}
void solve(){
cnt=0; num=0;
int n; cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
tr[i].push_back(a[i]);
du[a[i]]++;
}
for(int i=1;i<=n;i++) cin>>c[i];
vector<int>ans;
queue<int>q;
for(int i=1;i<=n;i++){
if(du[i]==0){
q.push(i);
}
}
while(!q.empty()){
int u=q.front(); q.pop();
ans.push_back(u);
for(auto v:tr[u]){
du[v]--;
if(du[v]==0) q.push(v);
}
}
for(int i=1;i<=n;i++){
if(dfn[i]==0) tarjan(i);
}
if(num>N-1){
while(1);
}
for(int i=1;i<=num;i++){
if(g[i].size()>1){
int num_=0;
int mina=1e9+1;
for(auto v:g[i]){
if(c[v]<mina){
mina=c[v]; num_=v;
}
}
int p=tr[num_][0];
while(p!=num_){
ans.push_back(p);
p=tr[p][0];
}
ans.push_back(num_);
}
}
for(auto v:ans){
cout<<v<<" ";
}
cout<<"\n";
for(int i=1;i<=n;i++){
g[i].clear();
tr[i].clear(); vis[i]=0; du[i]=0;
st[i]=dfn[i]=low[i]=0; col[i]=0;
}
}
int main()
{
cin.tie(0); ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}