C LCS
最长公共子序列,神奇的构造,先把他们的公共部分求出来,也就是最小值,然后在每个前面加上这么多个a,然后再依次看a,b,c,依次加入不同的字母,最后如果长度超过了n,肯定不存在。
#include<bits/stdc++.h>
using namespace std;
int a,b,c,n;
int main()
{
string s1,s2,s3;
s1 = "", s2 = "",s3 = "";
cin >> a >> b >> c >> n;
int t = min({a,b,c});
for(int i = 0;i < t;i ++ ) s1 += 'a', s2 += 'a', s3 += 'a';
a -= t,b -= t,c -= t;
for(int i = 0;i < a;i ++ ) s1 += 'b', s2 += 'b';
for(int i = 0;i < b;i ++ ) s2 += 'c', s3 += 'c';
for(int i = 0;i < c;i ++ ) s1 += 'd', s3 += 'd';
if(s1.size() > n || s2.size() > n || s3.size() > n)
{
puts("NO");
return 0;
}
while(s1.size() < n) s1 += 'x';
while(s2.size() < n) s2 += 'y';
while(s3.size() < n) s3 += 'z';
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
return 0;
}
E Tree Xor
这题能大概懂,但是代码还是不会写,01字典树也会,但是线段树好久没用了,只会用来求普通的区间修改和查询,稍微复杂点的就不知道怎么用了,其实是不知道正面维护信息,现附上一个我大概能看懂的代码,后面再学线段树的时候再回来看看。
#include<bits/stdc++.h>
#define N 100005
#define il inline
#define ll long long
#define debug puts("fuck");
using namespace std;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x;
}
int n,root,tot,q,cnt;
int L[N],R[N];
int tl[N*30*2],tr[N*30*2],sum[N*30*2];
int head[N],nex[N<<1],to[N<<1],val[N<<1];
const int lim=(1<<30)-1;
void add(int &rt,int l,int r,int ql,int qr,int d)
{
if(!rt) rt=++tot;
if(r-l+1==sum[rt]) return;
if(ql<=l&&r<=qr){sum[rt]=r-l+1;return;}
int mid=(l+r)>>1;
if(ql<=mid)
{
if(q&(1<<d)) add(tr[rt],l,mid,ql,qr,d-1);
else add(tl[rt],l,mid,ql,qr,d-1);
}
if(qr>mid)
{
if(q&(1<<d)) add(tl[rt],mid+1,r,ql,qr,d-1);
else add(tr[rt],mid+1,r,ql,qr,d-1);
}
sum[rt]=sum[tl[rt]]+sum[tr[rt]];
}
void dfs(int u,int f,int k)
{
q=k;
if(L[u]-1>=0) add(root,0,lim,0,L[u]-1,29);
if(R[u]+1<=lim) add(root,0,lim,R[u]+1,lim,29);
for(int i=head[u];i;i=nex[i])
if(to[i]!=f) dfs(to[i],u,k^val[i]);
}
int main()
{
n=read();int u,v,w;
for(int i=1;i<=n;++i) L[i]=read(),R[i]=read();
for(int i=1;i<n;++i)
{
u=read(),v=read(),w=read();
++cnt;to[cnt]=v;val[cnt]=w;nex[cnt]=head[u];head[u]=cnt;
++cnt;to[cnt]=u;val[cnt]=w;nex[cnt]=head[v];head[v]=cnt;
}
dfs(1,0,0);
cout<<lim+1-sum[root]<<endl;
return 0;
}
F Just a joke
签到题。两种操作,删除一条边,或者删除一个连通块,会使得边数-1或者-(k-1),
最后判断一下n+m的奇偶就行了。
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main()
{
cin >> n >> m;
for(int i = 0;i < m;i ++ )
{
int a,b;
cin >> a >> b;
}
if((n + m) % 2 == 0) puts("Bob");
else puts("Alice");
}
I Inverse Pair
这是一个关于逆序对的题。首先可以用树状数组求出当前序列的逆序对,然后再来分析可以减少的逆序对的数量。对于当前这个数,如果这个数前面出现了比他大1的数,我们将这个数加1,就会将这个数逆序数减1,但是如果不符合这种情况,就没必要+1,所以可以再用依次树状数组,就判断当前数+1是否已经存在过了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6;
int a[N];
int n;
int tr[4 * N];
int lowbit(int x)
{
return x & (-x);
}
void add(int x,int c)
{
for(int i = x;i <= n;i += lowbit(i)) tr[i] += c;
}
ll sum(int x)
{
ll ans = 0;
for(int i = x;i > 0;i -= lowbit(i)) ans += tr[i];
return ans;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i ++ )
{
scanf("%d",&a[i]);
}
ll res = 0;
for(int i = 1;i <= n;i ++ )
{
add(a[i],1);
res += (ll)sum(n) - sum(a[i]);
}
memset(tr,0,sizeof tr);
ll s = 0;
for(int i = 1;i <= n;i ++ )
{
if(i != 1)
{
if((sum(a[i] + 1) - sum(a[i])) > 0)
{
add(a[i] + 1,1);
s ++;
//cout << sum(a[i] + 1) - sum(a[i]) << endl;
}
else
{
add(a[i],1);
}
}
if(i == 1) add(a[i],1);
//cout << s << endl;
}
//cout << res << endl;
printf("%lld\n",res - s);
}
J Average
之前看这么多人做出来!!!然后想了一下列个式子,就可以知道直接就是求A,B的最大平均值之和,然后又不知道最大平均值怎么求了。后面搜到了,二分答案,用当前数减去平均值再求前缀和,判断前缀和是否大于零就可以得到当前这个平均值是大了还是小了,如果大于零,说明还有进步的空间,mid小了,l=mid,else 人= mid。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
double sum[N];
double solve(double a[],int n,int x)
{
for(int i = 0;i <= n;i ++ ) sum[i] = 0;
double l = -1e6, r = 1e6;
while((r - l) > 1e-7)
{
double mid = (l + r) / 2;
sum[0] = 0;
for(int i = 1;i <= n;i ++ ) sum[i] = sum[i - 1] + a[i] - mid;
double MIN = 1e10, MAX = -1e10;
for(int i = x;i <= n;i ++ )
{
MIN = min(MIN,sum[i - x]);
MAX = max(MAX,sum[i] - MIN);
}
if(MAX >= 0) l = mid;
else r = mid;
}
return l;
}
int main()
{
double a[N],b[N];
int n,m,x,y;
scanf("%d %d %d %d",&n,&m,&x,&y);
for(int i = 1;i <= n;i ++ ) scanf("%lf",&a[i]);
for(int i = 1;i <= m;i ++ ) scanf("%lf",&b[i]);
double res = 0.0;
res = solve(a,n,x) + solve(b,m,y);
printf("%.9lf\n",res);
return 0;
}
补充 P1419 寻找段落 洛谷
这题是J的补充,J是规定长度至少为L,所以我们每次只需要减去0 - i - L的最小值就行了。但是这道题还规定了长度不能超过T,也就是 只能找I-S 到i-T,的最小值,就可以用单调队列维护,可以看一下acwing的滑动窗口https://www.acwing.com/problem/content/156/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
int a[N];
double sum[N];
int q[N];
int S,T;
bool check(double x)
{
sum[0] = 0;
for(int i = 1;i <= n;i ++ ) sum[i] = sum[i - 1] + (double)a[i] - x;
int hh = 0, tt = -1;
for(int i = S;i <= n;i ++ )
{
if(hh <= tt && (i - T) > q[hh]) hh ++;
while(hh <= tt && sum[q[tt]] >= sum[i - S]) tt --;
q[++ tt] = i - S;
if(sum[i] - sum[q[hh]] >= 0) return true;
}
return false;
}
int main()
{
scanf("%d",&n);
scanf("%d %d",&S,&T);
for(int i = 1;i <= n;i ++ ) scanf("%d",&a[i]);
double l = -1e4, r = 1e4;
while(r - l > 1e-4)
{
double mid = (l + r) / 2;
if(check(mid)) l = mid;
else r = mid;
}
printf("%.3lf\n",l);
return 0;
}