Codeforces Round #453 (Div. 1)解題報告(ABCD)
A.Hashing Trees
題目大意:給定陣列a[i],其中a[i]表示深度為i的節點有a[i]個,問是否存在兩個不同構的樹同時滿足這個條件。如果有,請輸出。
簡要題解:有很多種構造方案。我的做法是,第一次把所有深度為i的節點都向深度為i-1的第一個節點連邊。第二次,嘗試分出一個深度為i的節點向深度為i-1的第二個節點連邊。如果可以這樣生成兩棵樹,則有解。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#define maxn 200010
using namespace std;
vector<int> v[maxn];
int a[maxn],f[maxn];
int n,h,cnt;
bool w=1;
int main()
{
scanf("%d",&h);
for (int i=0;i<=h;i++) scanf("%d",&a[i]);
cnt=1;v[0].push_back(1);
for (int i=1;i<=h;i++)
{
if (w && a[i]!=1 && v[i-1].size()>1)
{
w=0;v[i].push_back(++cnt);f[cnt]=v[i-1][1];
for (int j=1;j<a[i];j++) v[i].push_back(++cnt),f[cnt]=v[i-1][0];
}
else for (int j=1;j<=a[i];j++) v[i].push_back(++cnt),f[cnt]=v[i-1][0];
}
if (w) puts("perfect");
else
{
puts("ambiguous");
printf("0");cnt=1;
for (int i=1;i<=h;i++)
{
for (int j=1;j<=a[i];j++) printf(" %d",cnt);
cnt+=a[i];
}
printf("\n");
printf("0");
for (int i=1;i<=h;i++)
{
for (int j=0;j<v[i].size();j++) printf(" %d",f[v[i][j]]);
}
printf("\n");
}
return 0;
}
B. GCD of Polynomials
題目大意:構造兩個度數不超過n的多項式,使他們求GCD時,呼叫GCD過程的次數恰好為n。要求每一項的係數為-1或0或1。
簡要題解:注意到,每呼叫一次GCD過程,多項式的次數都會減一。所以最後的答案多項式一定是n次和n-1次的。考慮用最初的x和1,遞推到n次。我使用的做法是,每次列舉將高次多項式乘(x+(-1/0/1)),低次多項式乘-1/1,構造出新的高次多項式。最後構造出解。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<bitset>
using namespace std;
int da[6]={-1,-1,0,0,1,1};
int db[6]={1,-1,1,-1,1,-1};
int n,m;
int a[210][210],b[210][210];
int c[210];
bool solve(int x)
{
if (x==n) return 1;
x++;
for (int k=0;k<6;k++)
{
a[x][0]=da[k]*a[x-1][0]+db[k]*b[x-1][0];
for (int i=1;i<=x;i++) a[x][i]=a[x-1][i-1]+da[k]*a[x-1][i]+db[k]*b[x-1][i];
bool w=1;
for (int i=0;i<=x;i++) if (a[x][i]>=2 || a[x][i]<=-2) {w=0;break;}
if (w)
{
for (int i=0;i<x;i++) b[x][i]=a[x-1][i];
if (solve(x)) return 1;
}
}
return 0;
}
int main()
{
scanf("%d",&n);
a[1][1]=1;b[1][0]=1;
if (solve(1))
{
printf("%d\n",n);
for (int i=0;i<=n;i++) printf("%d ",a[n][i]);printf("\n");
printf("%d\n",n-1);
for (int i=0;i<n;i++) printf("%d ",b[n][i]);printf("\n");
}
else puts("-1");
return 0;
}
C.Bipartite Segments
題目大意:給定一個無偶環的無向圖,m次詢問,每次詢問[l,r]中能取出多少個子區間[x,y],滿足只選擇[x,y]中的點和邊,不會形成奇環。
簡要題解:以每個位置i,都有一個最靠左的端點pre[i],滿足[pre[i],i]的所有後綴都滿足不形成奇環。因為原圖沒有偶環,所以只要形成環就不滿足條件,問題簡單了許多。pre[i]是單調遞增的,故可以用整體二分來求。對於每組詢問,只需要維護一個字首和就可以解決。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#define maxn 300010
using namespace std;
vector<int> v[maxn];
int f[maxn],pre[maxn],st[maxn];
bool vis[maxn];
long long sum1[maxn],sum2[maxn];
int n,m,T,top;
int find(int i)
{
if (f[i]==i) return i;
if (!vis[i]) st[++top]=i,vis[i]=1;
return f[i]=find(f[i]);
}
void solve(int l,int r,int L,int R)
{
if (l>r) return;
if (L==R)
{
for (int i=l;i<=r;i++) pre[i]=L;
return;
}
int mid=(L+R)>>1;
int ans=r+1;
bool w=1;
for (int i=mid;i<=r;i++)
{
int j=lower_bound(v[i].begin(),v[i].end(),mid)-v[i].begin();
for (;j<v[i].size();j++)
{
int f1=find(v[i][j]),f2=find(i);
if (f1!=f2)
{
f[f1]=f2;
if (!vis[f1]) st[++top]=f1,vis[f1]=1;
}
else {w=0;break;}
}
if (!w) {ans=i;break;}
}
while (top) f[st[top]]=st[top],vis[st[top]]=0,top--;
solve(l,ans-1,L,mid);solve(ans,r,mid+1,R);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
v[max(x,y)].push_back(min(x,y));
}
for (int i=1;i<=n;i++) sort(v[i].begin(),v[i].end());
for (int i=1;i<=n;i++) f[i]=i;
solve(1,n,1,n);
//for (int i=1;i<=n;i++) printf("%d ",pre[i]);printf("\n");
for (int i=1;i<=n;i++) sum1[i]=sum1[i-1]+i,sum2[i]=sum2[i-1]+(i-pre[i]+1);
scanf("%d",&T);
while (T--)
{
int l,r;
scanf("%d%d",&l,&r);
long long ans=0;
int now=upper_bound(pre+l,pre+r+1,l)-pre;
//printf("%d\n",now);
ans=sum1[now-1]-sum1[l-1]-1ll*(now-l)*(l-1)+sum2[r]-sum2[now-1];
printf("%I64d\n",ans);
}
return 0;
}
D.Weighting a Tree
題目大意:一個無向連通圖,每個點有-n到n的權值,構造一種方案,給每條邊賦值,使每個點相鄰的邊的權值和等於這個點的權值。
簡要題解:如果這個圖是個二分圖,那麼無論如何賦值,左右兩邊的和必然相等,則取原圖的一棵生成樹,只使用樹邊來進行賦值即可。如果原圖不是二分圖,我們可以找到一條連線同一部分的邊,那麼把這條邊的權值賦為兩部分差的一半即可。按照二分圖進行分類的方法,也是很厲害了。
<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 100010
using namespace std;
int head[maxn],nxt[maxn*2],to[maxn*2],fa[maxn],fr[maxn];
int dep[maxn];
long long ans[maxn],w[maxn];
int n,m,num,e;
long long sum[2];
void addedge(int x,int y)
{
num++;to[num]=y;nxt[num]=head[x];head[x]=num;
}
void dfs(int x)
{
sum[dep[x]&1]+=w[x];
for (int p=head[x];p;p=nxt[p])
if (to[p]!=fa[x])
{
if (!dep[to[p]]) dep[to[p]]=dep[x]+1,fa[to[p]]=x,fr[to[p]]=p,dfs(to[p]);
else if ((dep[x]-dep[to[p]]+1)&1) e=p;
}
}
void solve(int x)
{
for (int p=head[x];p;p=nxt[p])
if (fa[to[p]]==x) solve(to[p]),w[x]-=ans[p/2];
ans[fr[x]/2]=w[x];w[x]-=ans[fr[x]/2];
}
int main()
{
num=1;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%I64d",&w[i]);
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);addedge(y,x);
}
dep[1]=1;dfs(1);
if ((sum[1]-sum[0])&1) {puts("NO");return 0;}
if (!e && sum[1]!=sum[0]) {puts("NO");return 0;}
if (e && sum[1]!=sum[0])
{
if (dep[to[e]]&1) ans[e/2]=(sum[1]-sum[0])/2,sum[1]=sum[0];
else ans[e/2]=(sum[0]-sum[1])/2,sum[0]=sum[1];
w[to[e]]-=ans[e/2];
w[to[e^1]]-=ans[e/2];
}
solve(1);
puts("YES");
for (int i=1;i<=m;i++) printf("%I64d\n",ans[i]);
return 0;
}