这次EDU的E有点邪门,感觉需要系统的再复习一下字符串,然后了解到还有kmp自动机和可持久化kmp这些东西,打算学习一下再写E
Educational Codeforces Round 134
C
题意:给定不降序的数组a和数组b,如果说两个数组之间的差值(b[i]-a[i])是d[i],那么在保证所有d[i]大于等于0的情况下,能够获得的每项d[i]的最大值和最小值是什么。
思路:最小值还是比较显然的,可以nlogn做,我们对于目前的a[i]可以从b数组里找到第一大于等于这个a[i]的值做差即可。
对于最大值,假设我们让a[1]和b[5]配对,那么显然,a[5]是空出来的,用谁填补最能满足合法性呢,显然是小于等于b[5]的第一个数,b[4],那么就有这样的递推关系:如果b[i]>a[i+1],那么a[i]就可以使用b[i+1]同时a[i+1]可以使用b[i],仍能满足d数组的合法性,这样一来就可以再将公式推广,查找每一个位置最远能够和谁交换。
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 2e5+100;
int a[N];
int b[N];
int maxd[N],mind[N];
int vis[N];
int dsu[N];
int tfind(int x)
{
if(dsu[x]==x)
return x;
return dsu[x]=tfind(dsu[x]);
}
void tmerge(int x,int y)
{
x=tfind(x),y=tfind(y);
dsu[y]=x;
}
signed main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int t;
for(cin>>t;t;t--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
dsu[i]=i;
}
for(int i=1;i<=n;i++)
{
cin>>b[i];
}
for(int i=n;i>=1;i--)
{
int pos=lower_bound(b+1,b+n+1,a[i])-b;
mind[i]=b[pos]-a[i];
}
for(int i=1;i<=n-1;i++)
{
if(b[i]>=a[i+1])
{
int x=tfind(i),y=tfind(i+1);
dsu[x]=y;
}
}
for(int i=1;i<=n;i++)
{
maxd[i]=b[tfind(i)]-a[i];
}
for(int i=1;i<=n;i++)
cout<<mind[i]<<" ";
cout<<endl;
for(int i=1;i<=n;i++)
cout<<maxd[i]<<" ";
cout<<endl;
}
return 0;
}
D
题意:给定a和b两个数组,那么a和b数组对应项异或后为c数组,c数组每一项做且运算后得到ans。求ans的最大值。
思路:显然,如果想要保留一个二进制位,那么对于这个二进制位,a数组和b数组必须不能让对应项的此个二进制位相等,否二取异或后为0,c数组取且运算后就会让此二进制位在ans中消失。
我们既然要维护ans最大,那么肯定要尽可能多的先保留大的二进制位,对于此次匹配的二进制位,先将ans加上这个二进制位,如果我们获得a数组中和ans二进制位相同的数等于b数组和ans二进制位不同的数,那么证明存在一种排列方式能够使得ans保留这个二进制位。
复杂度:30 *n * logn
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
int a[N],b[N];
int ansa[N],ansb[N];
int main()
{
int t;
for(cin>>t;t;t--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
cin>>b[i];
int ans=0;
for(int i=29;i>=0;i--)
{
ans+=(1<<i);
for(int j=1;j<=n;j++)
ansa[j]=a[j]&ans;
for(int j=1;j<=n;j++)
ansb[j]=ans-(ans&b[j]);
sort(ansa+1,ansa+n+1);
sort(ansb+1,ansb+n+1);
for(int j=1;j<=n;j++)
{
if(ansa[j]!=ansb[j])
{
ans-=(1<<i);
break;
}
}
}
cout<<ans<<endl;
}
}
百度之星第一场第三题
与其看这个题不如看看他的加强版原题。
G. Two Merged Sequences
题意:这里有个数组,我们认为可以把它分成一个单调递增和一个单调递减的数组,如果可以分成这样的两个数组,输出yes并且请你说明每个元素属于哪个数组,否则输出no
思路,,没太搞明白,先让我想想。。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
bool p[N][4], ans[N];
int n, a[N], dp[N][4];
main () {
ios::sync_with_stdio(false);
cin.tie(NULL);
cin >> n;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
}
dp[1][0] = 1e9;
dp[1][1] = -1e9;
for (int i = 2; i <= n; ++i)
{
dp[i][0] = -1e9, dp[i][1] = 1e9;
if (a[i - 1] < a[i])
{
dp[i][0] = dp[i - 1][0];
p[i][0] = 0;
}
if (a[i - 1] > a[i])
{
dp[i][1] = dp[i - 1][1];
p[i][1] = 1;
}
if (dp[i - 1][1] < a[i])
{
if (dp[i][0] < a[i - 1])
{
dp[i][0] = a[i - 1];
p[i][0] = 1;
}
}
if (dp[i - 1][0] > a[i]) {
if (dp[i][1] > a[i - 1]) {
dp[i][1] = a[i - 1];
p[i][1] = 0;
}
}
}
if (dp[n][0] == -1e9 && dp[n][1] == 1e9) {
cout << "NO\n"; exit(0);
}
int t = 0;
if (dp[n][0] != -1e9) t = 0;
if (dp[n][1] != 1e9) t = 1;
for (int i = n; i >= 1; --i) {
ans[i] = t;
t = p[i][t];
}
cout << "YES\n";
for (int i = 1; i <= n; ++i) {
cout << ans[i] << " ";
}
}
GXNU校赛
一个遍历树的题和一个算区间GCD的题,还有一个三分的题还算挺好的。
https://ac.nowcoder.com/acm/contest/39842/G
搜索题,一开始没反应过来是要干什么,实际上m的操作就是强行改变了当前不能走的节点的左右儿子属性然后强行走下去了。。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+100;
struct node
{
int nex,to;
};
node edge[N<<1];
int n,m,tot;
int head[N],vis[N],dir[N],w[N];
void add(int from,int to)
{
edge[++tot].nex=head[from];
edge[tot].to=to;
head[from]=tot;
}
int ans,pos=1e5+100;
void DFS(int now,int sum,int lr,int cnt)
{
if(!vis[now])
{
if(ans<sum)
ans=sum,pos=now;
else if(ans==sum)
pos=pos>now?now:pos;
return;
}
for(int i=head[now];i;i=edge[i].nex)
{
int to=edge[i].to;
if(dir[to]!=lr)
{
DFS(to,sum+w[to],dir[to],cnt);
}
else if(cnt<m)
{
DFS(to,sum+w[to],dir[to]^1,cnt+1);
}
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>w[i];
for(int i=1,a,b,st;i<=n-1;i++)
{
cin>>a>>b>>st;
add(a,b);
vis[a]=1;
dir[b]=st-1;
}
DFS(1,w[1],0,0);
DFS(1,w[1],1,0);
cout<<pos<<endl<<ans<<endl;
return 0;
}
离线预处理所有的可能情况,然后直接输出答案。
比如数组 1 3 5 7
我们要求一个gcd是x的时候,需要搞定所有的区间比如1到4,这时我们希望算1到4的gcd能够简化一下,简化成1到3的gcd和4做gcd。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N=1e5+100;
int a[N];
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
signed main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int t;
for(cin>>t;t;t--)
{
unordered_map<int,int>mp[N],ans;
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
ans[a[1]]++;
mp[1][a[1]]++;
for(int i=2;i<=n;i++)
{
mp[i][a[i]]++;
ans[a[i]]++;
for(auto x:mp[i-1])
{
int t=gcd(x.first,a[i]);
mp[i][t]+=x.second;
ans[t]+=x.second;
}
}
for(int i=1,x;i<=m;i++)
{
cin>>x;
cout<<ans[x]<<endl;
}
}
return 0;
}
签到在这里
三分法求函数在一个区间上的极小值。利用三分法的模板即可,题面烂了,然后就是注意精度,再就是个板子,没了。
// copied
#include<bits/stdc++.h>
using namespace std;
#define int long long
const long double eps = 1e-15;
int a, b, c, d, e;
long double f(long double x)
{
return a * x * x + b * x + c + d * log2l(x) * log2l(x) + e * log2l(x);
}
void solve()
{
int m;
cin >> m;
while(m -- )
{
cin >> a >> b >> c >> d >> e;
if(d == 0 && e != 0)
{
cout << "INF\n";
continue;
}
long double l = 0.0000001, r = 1e6; // 根据题目改动区间范围
long double lmid, rmid;
while(r - l > eps)
{
lmid = l + (r - l) / 3.0; // 左三等分点
rmid = r - (r - l) / 3.0; // 右三等分点
if(f(lmid) > f(rmid)) l = lmid; // 根据函数的单调性和极值的性质设定写判断函数,此处为极小值点
else r = rmid;
}
cout << fixed << setprecision(8) << f(l) << '\n';
}
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0), cout.tie(0);
int T;
cin >> T;
while(T -- ) solve();
}