【CF刷题】14-05-12
Round 236 div.1
A:只需要每个点连接所有比他大的点,知道边用完为止。
//By BLADEVIL #include <cmath> #include <cstdio> #define maxn 25; using namespace std; int main() { int task; scanf("%d",&task); while (task--) { int n,p; scanf("%d%d",&n,&p); int rest(2*n+p); for (int i=1;(i<=n)&&rest;i++) for (int j=i+1;(j<=n)&&rest;j++) printf("%d %d\n",i,j),rest--; } return 0; }
B:求出g[i]=gcd(a[1],a[2]....a[i]),那么只需要从后向前贪心的考虑每个g[i]能不能被除掉就好了。
//By BLADEVIL #include <cstdio> #include <algorithm> #define maxn 5010 using namespace std; int n,m; int a[maxn],b[maxn],g[maxn]; int calc(int x) { int ans(0); for (int i=1;i<=m;i++) while (!(x%b[i])) ans--,x/=b[i]; for (int i=2;i*i<=x;i++) while (!(x%i)) ans++,x/=i; if (x>1) ans++; return ans; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]),g[i]=__gcd(g[i-1],a[i]); for (int i=1;i<=m;i++) scanf("%d",&b[i]); int cur(1); for (int i=n;i;i--) { a[i]/=cur; g[i]/=cur; if (calc(g[i])<0) cur*=g[i],a[i]/=g[i]; } //for (int i=1;i<=n;i++) printf("%d ",a[i]); printf("\n"); int ans(0); for (int i=1;i<=n;i++) ans+=calc(a[i]); printf("%d\n",ans); return 0; }
Round 238 div.1
A:我们发现其实最后的答案只与a[i][i]有关,那么我们只记录a[i]=a[i][i],根据操作模拟就可以了。
//By BLADEVIL #include <cstdio> #define maxn 1010 using namespace std; int n; int mat[maxn][maxn],a[maxn]; int main() { //freopen("data.txt","r",stdin); scanf("%d",&n); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&mat[i][j]); for (int i=1;i<=n;i++) a[i]=mat[i][i]; int ans(0); for (int i=1;i<=n;i++) ans=(ans+a[i])%2; int task; scanf("%d",&task); while (task--) { int x,y; scanf("%d",&x); if (x==3) printf("%d",ans); else { scanf("%d",&x); ans^=1; } } printf("\n"); return 0; }
B:我们可以将每个给定的a[i]-1,查询s-a[i]+1位置是否被占用,被占用我们cnt++,表示和为s-1的数对加一,否则直接输出s-a[i]+1,然后再找出cnt个和为s-1的数对输出。
//By BLADEVIL #include <cstdio> #define maxn 1000010 #define s 1000000 using namespace std; int n,tot; int flag[maxn]; int main() { scanf("%d",&n); for (int i=1;i<=n;i++) { int x; scanf("%d",&x); flag[x]=1; if (flag[s+1-x]) tot++; } printf("%d\n",n); for (int i=1;i<=s;i++) { if (tot&&!flag[i]&&!flag[s-i+1]) { tot--; flag[i]=flag[s-i+1]=1; printf("%d %d ",i,s-i+1); } if (flag[i]&&!flag[s-i+1]) printf("%d ",s-i+1); } printf("\n"); return 0; }
C:深搜,同时每个点记录这个点剩下的一条没有被选定的边,枚举这个点的所有儿子,如果儿子的没有被选定的边不为0我们就画连接儿子的边和儿子剩下的边,否则判断这个点是否有剩下的边,有的话输出连接儿子的边和剩下的边,最后返回剩下的边。
//By BLADEVIL #include <cstdio> #define maxn 100010 #define maxm 200010 using namespace std; int n,m,l; int pre[maxm],other[maxm],last[maxn]; int flag[maxn]; void connect(int x,int y) { pre[++l]=last[x]; last[x]=l; other[l]=y; } int dfs(int x,int fa) { flag[x]=1; int cur(0); for (int p=last[x];p;p=pre[p]) { if (other[p]==fa) continue; if (flag[other[p]]==2) continue; int edge; if (!flag[other[p]]) edge=dfs(other[p],x); else edge=0; //printf("|%d %d\n",x,edge); if (edge) printf("%d %d %d\n",x,other[p],edge); else if (cur) printf("%d %d %d\n",cur,x,other[p]),cur=0; else cur=other[p]; } flag[x]=2; //printf("%d %d\n",x,cur); return cur; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); connect(x,y); connect(y,x); } if (m&1) printf("No solution\n\n"); else dfs(1,-1); return 0; }
D:我们将每条线段能看到的最右面的线段设为这个线段的父亲,那么一个询问的答案就是这两个点的lca,对于父亲我们可以维护一个下凸壳来计算。
//By BLADEVIL #include <cstdio> #include <iostream> #include <algorithm> #define maxn 200010 #define LL long long using namespace std; struct rec { LL x,y; }a[maxn]; int n,t,l; int q[maxn],jump[maxn][20]; int pre[maxn],other[maxn],last[maxn],dep[maxn]; void connect(int x,int y) { pre[++l]=last[x]; last[x]=l; other[l]=y; } int lca(int x,int y) { if (dep[x]>dep[y]) swap(x,y); int det(dep[y]-dep[x]); for (int j=0;j<=18;j++) if (det&(1<<j)) y=jump[y][j]; //printf("%d %d\n",x,y); if (x==y) return x; for (int j=18;j>=0;j--) if (jump[x][j]!=jump[y][j]) x=jump[x][j],y=jump[y][j]; return jump[x][0]; } void dfs(int x) { for (int p=last[x];p;p=pre[p]) { dep[other[p]]=dep[x]+1; dfs(other[p]); } } int main() { cin>>n; for (int i=1;i<=n;i++) cin>>a[i].x>>a[i].y; for (int i=n;i;i--) { //for (int j=1;j<=t;j++) printf("%d ",q[j]); printf("\n"); while (t>1&&(a[q[t]].y-a[q[t-1]].y)*(a[i].x-a[q[t-1]].x)>(a[q[t]].x-a[q[t-1]].x)*(a[i].y-a[q[t-1]].y)) t--; jump[i][0]=q[t]; q[++t]=i; } //for (int i=1;i<=n;i++) printf("%d ",jump[i][0]); for (int i=1;i<=n;i++) connect(jump[i][0],i); dfs(0); //for (int i=1;i<=n;i++) printf("%d ",dep[i]); printf("\n"); for (int j=1;j<=18;j++) for (int i=1;i<=n;i++) jump[i][j]=jump[jump[i][j-1]][j-1]; int task; cin>>task; while (task--) { int x,y; cin>>x>>y; cout<<lca(x,y)<<' '; } cout<<endl; return 0; }
Round 240 div.1
A:假设n为偶数,奇数时最后一位随意,那么我们最后两个数为k-(n+2)/2和2*(k-(n+2)/2),前n-2个数为不和最后两位相同的连续的数就可以了。
//By BLADEVIL #include <cstdio> #define maxn 100010 using namespace std; int n,k; int a[maxn]; int main() { scanf("%d%d",&n,&k); if ((n==1)&&(k)) { printf("-1\n"); return 0; } if (n-(n&1)>2*k) { printf("-1\n"); return 0; } int cur((n-2-(n&1))/2); k-=cur; a[n]=1000001+k; for (int i=1,j=1;i<=n-2-(n&1);i+=2) { if (j==k) j+=100; if (j+1==k) j+=100; if (j==(k<<1)) j+=100; if (j+1==(k<<1)) j+=100; a[i]=j++; a[i+1]=j++; } a[n-(n&1)]=k,a[n-1-(n&1)]=k<<1; for (int i=1;i<=n;i++) printf(i==n?"%d\n":"%d ",a[i]); return 0; }
B:设w[i][j]为第i位为j的方案数,然后转移就可以了。
//By BLADEVIL #include <cmath> #include <cstdio> #define maxn 2010 #define d39 1000000007 using namespace std; int n,k; int w[maxn][maxn]; int main() { scanf("%d%d",&n,&k); for (int i=1;i<=n;i++) w[1][i]=1; for (int i=2;i<=k;i++) for (int j=1;j<=n;j++) for (int g=1;g<=sqrt(j);g++) if (!(j%g)) { w[i][j]=(w[i][j]+w[i-1][g])%d39; if (g*g!=j) w[i][j]=(w[i][j]+w[i-1][j/g])%d39; } int ans(0); for (int i=1;i<=n;i++) ans=(ans+w[k][i])%d39; printf("%d\n",ans); return 0; }
C:我们可以将一次翻转看成将左右儿子翻转然后交换左右儿子,我们可以发现每个块内的翻转对块与块之间的答案没有影响,那么我们只需要按照归并的过程求出长度为2^j的块与块合并的时候产生的答案,和交换块产生的答案,那么每次翻转我们就可以看做将j之前的所有当前状态取反,计算代价即可。
//By BLADEVIL #include <cstdio> #include <cstring> #include <iostream> #define maxn (1<<20)+100 #define LL long long using namespace std; LL n,len; LL a[maxn],b[maxn]; LL w[30][2],flag[30]; void work(LL p,LL cur) { if (p>n) return ; for (LL i=1;i<=len;i+=cur) { LL k1(i),k2(i+(cur>>1)),det(0); while ((k1<i+(cur>>1))||(k2<i+cur)) { if ((k1==i+(cur>>1))||((a[k2]<a[k1])&&(k2<i+cur))) { w[p][0]+=i+(cur>>1)-k1; b[i+det]=a[k2]; k2++; } else { b[i+det]=a[k1]; k1++; } det++; } k1=i+(cur>>1); k2=i; while ((k1<i+cur)||(k2<i+(cur>>1))) { if ((k1==i+cur)||((a[k2]<a[k1])&&(k2<i+(cur>>1)))) { w[p][1]+=i+cur-k1; k2++; } else k1++; } } memcpy(a,b,sizeof b); work(p+1,cur<<1); } int main() { cin>>n; len=1; for (LL i=1;i<=n;i++) len*=2; for (LL i=1;i<=len;i++) cin>>a[i]; work(1,2); LL ans(0); for (LL i=1;i<=n;i++) ans+=w[i][0]; //for (LL i=1;i<=n;i++) printf("%d ",w[i][0]); printf("\n"); //for (LL i=1;i<=n;i++) printf("%d ",w[i][1]); printf("\n"); for (LL i=1;i<=n;i++) flag[i]=1; //printf("%d\n",ans); LL task; cin>>task; while (task--) { LL x; cin>>x; for (LL i=1;i<=x;i++) ans+=w[i][flag[i]]-w[i][flag[i]^1]; for (LL i=1;i<=x;i++) flag[i]^=1; cout<<ans<<endl; } return 0; }
Round 245 div.1
A:tree-dp,设w[i][0..1]表示这个节点的状态和最后的相同(1),不相同(0),且每两层的儿子节点都符合这个状态的最小代价,然后转移就可以了,其实这道题相当于两棵互不影响的树,分别做tree-dp。
//By BLADEVIL #include <cstdio> #define maxn 100010 #define maxm 200020 using namespace std; int n,l; int a[maxn],b[maxn]; int other[maxm],last[maxn],pre[maxm]; int que[maxn],dep[maxn]; int w[maxn][2]; void connect(int x,int y) { pre[++l]=last[x]; last[x]=l; other[l]=y; } void dfs(int x,int cur) { if (a[x]^b[x]^(!cur)) printf("%d\n",x),cur^=1; for (int p=last[x];p;p=pre[p]) if (dep[other[p]]>dep[x]) for (int q=last[other[p]];q;q=pre[q]) if (dep[other[q]]>dep[other[p]]) dfs(other[q],cur); } int main() { scanf("%d",&n); for (int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); connect(x,y); connect(y,x); } for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) scanf("%d",&b[i]); int h(0),t(1); que[1]=1; dep[1]=1; while (h<t) { int cur=que[++h]; for (int p=last[cur];p;p=pre[p]) { if (dep[other[p]]) continue; que[++t]=other[p]; dep[other[p]]=dep[cur]+1; } } for (int i=n;i;i--) { int x=que[i]; for (int p=last[x];p;p=pre[p]) { if (dep[other[p]]<dep[x]) continue; //w[x][0]+=w[other[p]][1]; w[x][1]+=w[other[p]][1]; for (int q=last[other[p]];q;q=pre[q]) { if (dep[other[q]]<dep[other[p]]) continue; if (a[x]^b[x]) w[x][0]+=w[other[q]][0]; else w[x][0]+=w[other[q]][1]; if (a[x]^b[x]) w[x][1]+=w[other[q]][0]; else w[x][1]+=w[other[q]][1]; } } if (a[x]^b[x]) w[x][1]++; else w[x][0]++; } //for (int i=1;i<=n;i++) printf("%d %d\n",w[i][0],w[i][1]); int ans(w[1][1]); for (int p=last[1];p;p=pre[p]) ans+=w[other[p]][1]; printf("%d\n",ans); dfs(1,1); for (int p=last[1];p;p=pre[p]) dfs(other[p],1); return 0; }
B:记录w[i][j][1..4]表示四个方向到i,j点的最大值,然后枚举相遇的节点就可以了。
//By BLADEVIL #include <cstdio> #include <iostream> #include <algorithm> #define maxn 1010 #define LL long long using namespace std; LL n,m; LL a[maxn][maxn]; LL w[5][maxn][maxn]; void prepare() { for (LL i=1;i<=n;i++) for (LL j=1;j<=m;j++) w[1][i][j]=max(w[1][i][j-1],w[1][i-1][j])+a[i][j]; for (LL i=1;i<=n;i++) for (LL j=m;j;j--) w[2][i][j]=max(w[2][i][j+1],w[2][i-1][j])+a[i][j]; for (LL i=n;i;i--) for (LL j=1;j<=m;j++) w[3][i][j]=max(w[3][i+1][j],w[3][i][j-1])+a[i][j]; for (LL i=n;i;i--) for (LL j=m;j;j--) w[4][i][j]=max(w[4][i+1][j],w[4][i][j+1])+a[i][j]; } int main() { cin>>n>>m; for (LL i=1;i<=n;i++) for (LL j=1;j<=m;j++) cin>>a[i][j]; prepare(); LL ans(0); for (LL i=2;i<n;i++) for (LL j=2;j<m;j++) ans=max(ans,w[1][i-1][j]+w[4][i+1][j]+w[2][i][j+1]+w[3][i][j-1]), ans=max(ans,w[1][i][j-1]+w[4][i][j+1]+w[2][i-1][j]+w[3][i+1][j]); cout<<ans<<endl; return 0; }
C:深搜就可以了,有一种贪心不正确但是数据弱可以过去。
#include <cstdio> #include <iostream> #include <algorithm> #define maxn 30 using namespace std; int n; int w[maxn],p[maxn]; int dfs(int x,int used) { //printf("%d %d\n",x,used); if (x>n) return *max_element(p+1,p+1+n)==0; for (int i=1;i<=x;i++) if ((p[i]>w[x]||p[i]==w[x]&&p[i]!=w[i]-1)&&((used&(1<<p[i])))==0) { p[i]-=w[x]; if (dfs(x+1,x+1<=n&&w[x]==w[x+1]?used:0)) return 1; p[i]+=w[x]; used|=1<<p[i]; } return 0; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&w[i]); sort(w+1,w+1+n,greater<int>()); for (int i=1;i<=n;i++) p[i]=w[i]-1; printf(dfs(2,0)?"YES\n":"NO\n"); }