本人比較的菜,D題是比賽結束後十分鐘才把代碼打完~ E,F不會~
A. Shuffle Hashing
A題暴力即可
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e3+10;
char s[N],t[N];
int main()
{
int _;cin>>_;while(_--)
{
scanf("%s%s",s+1,t+1);
int n=strlen(s+1);
int m=strlen(t+1);
int f=1;
int vis[30],vs[30];
for(int i=0;i<26;++i) vis[i]=0,vs[i]=0;
for(int i=1;i<=n;++i) vis[s[i]-'a']++;
for(int i=1;i+n-1<=m&&f;++i){
for(int i=0;i<26;++i) vs[i]=0;
for(int j=i;j<=i+n-1;++j){
vs[t[j]-'a']++;
}
bool flag=1;
for(int j=0;j<26&&flag;++j){
if(vs[j]!=vis[j]) {
flag=0;
//printf("j:%d\n",j);
}
}
if(flag) f=0;
}
if(!f) puts("YES");
else puts("NO");
}
}
B. A and B
假設a<b
先將1,2,3,4,一直加到小的一部分。。。然後當 a>b 的時候,(a-b)%2==0 的時候,就在1,2,3,4.。。。中選一個偶數分給b即可。。。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e6+10;
ll a,b;
ll dp[N],num[N];
int main()
{
int _;cin>>_;while(_--)
{
scanf("%lld%lld",&a,&b);
if(a==b){
puts("0");
continue;
}
if(a<b) swap(a,b);
ll d=a-b;
ll sum=0;
ll ans=1;
while(sum<d||((sum-d)%2!=0)){
sum+=ans;
ans++;
}
printf("%lld\n",ans-1);
}
}
C. Berry Jam
設x是紅色的個數,y是白色的個數
遍歷一遍 前綴記錄x-y,用map記錄位置即可。。
後綴遍歷計算x-y,在map中通過找-(x-y) 得到合法區間即可。。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e6+10;
int dp[N][3],f[N][3],n,a[N],b[N];
int main()
{
int _;cin>>_;while(_--)
{
scanf("%d",&n);
map<int,int>mp;
n=2*n;
int x=0,y=0;
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
int ans=n;
rep(i,1,n/2)
{
if(a[i]==1) x++;
else y++;
if(x==y) ans=min(ans,n-i);
mp[x-y]=i;
}
x=0,y=0;
for(int i=n;i>=n/2+1;--i){
if(a[i]==1) x++;
else y++;
if(x==y) ans=min(ans,i-1);
if(mp[-(x-y)]!=0) ans=min(ans,i-mp[-(x-y)]-1);
}
printf("%d\n",ans);
}
}
D. Segment Tree
做法1:線段樹(已被HACK)
hack數據:
5
1 4
2 5
3 6
7 9
8 10 求聯通塊有問題
樹的特點:邊數=n-1,然後是一個聯通塊。。
連通塊好計算,一層for求區間並即可
邊數怎麼求呢?
從前往後遍歷,遇到左區間,就線段樹上點更新,遇到右區間就查詢區間和,就是邊數。。
我寫了兩顆線段樹(正着一遍 反着一遍)來判斷是否有獨立的點,感覺沒必要,寫多了(有求連通塊就可以了)。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e6+10;
int n,vis[N],vs[N];
pi a[N],b[N];
ll sum[N*4],s[N*4];
void up(int id,int l,int r,int pos,int val,int ty)
{
if(l==r) {
if(ty) sum[id]+=val;
else s[id]+=val;
return ;
}
int mid=l+r>>1;
if(pos<=mid) up(id<<1,l,mid,pos,val,ty);
else up(id<<1|1,mid+1,r,pos,val,ty);
if(ty) sum[id]=sum[id<<1]+sum[id<<1|1];
else
s[id]=s[id<<1]+s[id<<1|1];
}
ll qu(int id,int l,int r,int ql,int qr,int ty)
{
if(ql<=l&&r<=qr){
if(ty)
return sum[id];
else return s[id];
}
ll res=0;
int mid=l+r>>1;
if(ql<=mid) res+=qu(id<<1,l,mid,ql,qr,ty);
if(qr>mid) res+=qu(id<<1|1,mid+1,r,ql,qr,ty);
return res;
}
int main()
{
cin>>n;
for(int i=1;i<=n;++i){
scanf("%d%d",&a[i].first,&a[i].second);
b[i].first=a[i].first;
b[i].second=a[i].second;
vis[a[i].first]=i;
vis[a[i].second]=i;
}
if(n==1){
puts("YES");
return 0;
}
sort(b+1,b+1+n);
int flag=1;
int l=b[1].first,r=b[1].second;
for(int i=1;i<=n&&flag;++i){
bool tmp=0;
if(b[i].first<=r) tmp=1;
l=min(l,b[i].first);
r=max(r,b[i].second);
if(!tmp) flag=0;
}
if(!flag){
puts("NO");
return 0;
}
ll ans=0;
for(int i=2*n;i>=1;--i){
int id=vis[i];
if(a[id].second==i){
up(1,1,2*n,a[id].second,1,1);
}
else{
ll t;
t=qu(1,1,2*n,a[id].first,a[id].second,1)-1;
up(1,1,2*n,a[id].second,-1,1);
if(t>0) ans+=t,vs[id]=1;
}
}
for(int i=1;i<=2*n;++i){
int id=vis[i];
if(a[id].first==i){
up(1,1,2*n,a[id].first,1,0);
}
else{
ll t;
t=qu(1,1,2*n,a[id].first,a[id].second,0)-1;
up(1,1,2*n,a[id].first,-1,0);
if(t>0) vs[id]=1;
}
}
int f=1;
for(int i=1;i<=n&&f;++i) if(vs[i]==0) f=0;
if(f&&ans==n-1) puts("YES");
else puts("NO");
}
做法2:
線段樹求聯通塊有問題,那麼就換種方式,開始想的是如何在線段樹節點上保存所有區間內的區間標號。。
看了別人的代碼,就是用set<pair<int,int> >st;二分當前區間有多少個相交的區間,結合並查集防止有環。
#include<bits/stdc++.h>
using namespace std;
#define pi pair<int,int>
typedef long long ll;
const int N=1e6+10;
pi a[N];
int fa[N];
set<pi >st;
int n;
int fin(int x)
{
if(fa[x]!=x) fa[x]=fin(fa[x]);
return fa[x];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d",&a[i].first,&a[i].second);
fa[i]=i;
}
sort(a+1,a+1+n);
int f=1;
ll ans=0;
for(int i=1;i<=n&&f;++i){
auto l=st.lower_bound(make_pair(a[i].first,0));
auto r=st.lower_bound(make_pair(a[i].second,0));
for(auto it=l;it!=r&&f;++it){
int f1=fin(i);
int f2=fin((*it).second);
if(f1==f2) f=0;
else{
fa[f1]=f2;
ans++;
}
}
st.insert(make_pair(a[i].second,i));
}
if(f&&ans==n-1) puts("YES");
else puts("NO");
}
E. Tests for problem D
這題跟D題相反,現給你一顆樹,能否構造一個D題一樣的區間。。
有相交但不包含的區間就有一條邊
l1 l2 r1 r2 代表着有一條邊 ,l1 l2 r2 l1不算
做法:很妙的做法,看代碼就懂了:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
vector<int>G[N];
int n,cnt,l[N],r[N];
void dfs(int u,int fa)
{
for(int v:G[u]){
if(v==fa) continue;
l[v]=++cnt;
}
r[u]=++cnt;
for(int i=G[u].size()-1;i>=0;--i){
if(G[u][i]==fa)continue;
dfs(G[u][i],u);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
l[1]=++cnt;
dfs(1,0);
for(int i=1;i<=n;++i) printf("%d %d\n",l[i],r[i]);
return 0;
}