您的位置:首页 > 其它

ustc三月月赛解题报告

2011-03-11 21:21 169 查看
AB水题,C是斜率优化的动态规划。

报告从D开始。

D 高精度乘法。

题目的bug带来了比较大的困扰,之后许多弱弱的小毛病也耽误了不少时间

#include <cstdio>
#include <cstdlib>
#include <cstring>
#define maxn 10010
int main(){
char a[maxn],b[maxn];
int ans[maxn], t1[maxn], t2[maxn];
int len1, len2, len, positive1, positive2;
while (scanf("%s%s", a, b)==2){
memset(t1, 0, sizeof(int)*maxn);
memset(t2, 0, sizeof(int)*maxn);
memset(ans, 0, sizeof(int)*maxn);
//a
for (int i=0;; i++)
if (a[i]=='/0'){len1=i-1; break;}
if (a[0]=='-'){positive1=false;len1--;}
else{positive1=true;}
for (int i=((positive1)?len1:len1+1), j=0; i>=0; i--, j++)
t1[j]=a[i]-'0';
//b
for (int i=0;; i++)
if (b[i]=='/0'){len2=i-1; break;}
if (b[0]=='-'){positive2=false;len2--;}
else{positive2=true;}
for (int i=((positive2)?len2:len2+1), j=0; i>=0; i--, j++)
t2[j]=b[i]-'0';
//len
len=(len1>len2)?len1:len2;
if (positive1==positive2){
//a+b or (-a)+(-b)   a>0 b>0
for (int i=len; i>=0; i--){
ans[i]=t1[i]+t2[i];
int j=i;
if (ans[j]>=10){
ans[j+1]+=ans[j]/10;
ans[j]%=10;
j++;
}
if (j>len) len=j;
}
while (ans[len]==0 && len>=0) len--;
if (ans[len]==0){
printf("0/n");
}
else{
if (!positive1) printf("-");
for (int i=len; i>=0; i--) printf("%d", ans[i]);
printf("/n");
}
}
else{
//if abs(t1) < abs(t2) exchange
bool exchange=false;
if (len1<len2) exchange=true;
else if (len1==len2){
for (int i=len1; i>=0; i--)
if (t1[i]<t2[i]){exchange=true;break;}
}
if (exchange){
int t;
for (int i=0; i<=len; i++){
t=t1[i]; t1[i]=t2[i]; t2[i]=t;
}
t=len1; len1=len2; len2=t;
t=positive1; positive1=positive2; positive2=t;
}
//t1-t2   abs(t1)>abs(t2)
for (int i=0; i<=len; i++)
ans[i]=(i<=len1?t1[i]:0)-(i<=len2?t2[i]:0);
for (int i=0; i<=len; i++)
if (ans[i]<0) {ans[i]+=10; ans[i+1]--;}
if (a[len+1]==-1){
if (positive2) positive1=true;
else positive1=false;
}else{
if (positive2) positive1=false;
else positive1=true;
}
while (ans[len]<=0 && len>=0) len--;
if (ans[len]==0){
printf("0/n");
}
else{
if (positive2) printf("-");
for (int i=len; i>=0; i--) printf("%d", ans[i]);
printf("/n");
}
}
}
return 0;
}


E 找每点最近三个点

赛后写的算法其实和比赛时思路是一致的。这个算法肯定不算是正统的, 不过解决一些随机产生的,分布比较平均的数据应该还是可以的(含有人品因素)。具体解法是:把所有点进行一个先比x再比y的排序,然后对每个点i在排序表里上/下交替地找一些点j进行比较,当i和j的x坐标差大于得到的最近三个距离的时候,就可以停止枚举了。

写的时候没有交替地在排序表里上/下寻找邻进点,时间花得比较多。

还有在精度问题上卡了非常久,最后还是放宽了很多地方才AC的。

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 20010
#define MAXINT (1<<30)
const int EPS=1E-6;
struct node{
int order;
int id;
double x,y;
}q[maxn];
int cmp(const void *a, const void *b){
struct node *aa=(node *)a;
struct node *bb=(node *)b;
return ((aa->x - bb->x)>-EPS?1:-1);
}
double dis(struct node a, const node b){
return ((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main(){
freopen("10k.in", "r", stdin);
freopen("ans.txt","w",stdout);
int ansid[maxn][3];
double ans[maxn][3];
int orid[maxn];         //origin id
int n=0;
while (scanf("%d%lf%lf", &q
.id, &q
.x, &q
.y)==3){
q
.order=n;
orid
=q
.id;
n++;
}
qsort(q, n, sizeof(q[0]), cmp);
for (int i=0; i<n; i++){
int order=q[i].order;
for (int j=0; j<3; j++) ans[order][j]=MAXINT;
int maxpos=1;                                               //ans[i]中最大的是ans[or][maxpos]
for (int j=i-1; j>=0; j--){
double t;
if (q[i].x-q[j].x-ans[order][maxpos]>1)break;
if (ans[order][maxpos]>(t=dis(q[i], q[j]))){
ans[order][maxpos]=t;
ansid[order][maxpos]=q[j].order;
for (int k=0; k<3; k++)                          //ans[or][k] > ans[or][maxpos]
if (ans[order][k]>ans[order][maxpos])
maxpos=k;
}
}
for (int j=i+1; j<n; j++){
double t;
if (q[j].x-q[i].x-ans[order][maxpos]>1)break;
if (ans[order][maxpos]>(t=dis(q[i], q[j]))){
ans[order][maxpos]=t;
ansid[order][maxpos]=q[j].order;
for (int k=0; k<3; k++)                          //ans[or][k] > ans[or][maxpos]
if (ans[order][k]>ans[order][maxpos])
maxpos=k;
}
}
}
for (int i=0; i<n; i++){
printf("%d ", orid[i]);
for (int j=0; j<3; j++){
int minid=0;
for (int k=1; k<3; k++)
if (ans[i][k]<ans[i][minid]) minid=k;
printf("%d", orid[ansid[i][minid]]);
if (j!=2) printf(",");
ans[i][minid]=1<<29;
}
printf("/n");
}
return 0;
}


F 集合的操作

这道题其实就是经典的集合操作,关键在于如何合并这些操作。

操作最终可以归结为染色和取反两种,分别写好就可以了。

并运算对应的是T部分的染1色。

交运算对应的是非T部分染颜色0,即S中不在T里的部分染成0。

S-T运算就是把T部分染成0色。

对称差运算对应的是T部分中属于S的染成0,不属于S的染成1。这里用到异或/取反。

T-S运算复杂一些:

先把T部分中属于S的染成0,不属于S的染成1。这里用到异或/取反。

再把整区域中非T的部分染成0

这一道题纠结了非常久主要是由于T-S部分的运算没有弄清楚,漏掉了一部分。却一直以为是线段树写的有问题,非常久了才找到问题所在。

#include <cstdio>
#include <cstdlib>
#include <cstring>
const int maxn=65536*3;
short c[maxn*4];
short fc[maxn+1];     //final color
void print(int k, int l, int r, int x, int y, int newc){
if (x>r || y<l || l>r) return;
if (l>=x && r<=y) c[k]=newc;
else{
int mid=(l+r)>>1, lson=k<<1, rson=(k<<1)+1;
if (c[k]!=-1){
c[lson]=c[rson]=c[k];
c[k]=-1;
}
print(lson, l, mid, x, y, newc);
print(rson, mid+1, r, x, y, newc);
if (c[lson]==c[rson] && c[lson]!=-1)
c[k]=c[lson];
}
}
void rev(int k, int l, int r, int x, int y){
if (x>r || y<l || l>r) return;
if (l>=x && r<=y && c[k]!=-1) c[k]^=1;
else{
int mid=(l+r)>>1, lson=k<<1, rson=(k<<1)+1;
if (c[k]!=-1){
c[lson]=c[rson]=c[k];
c[k]=-1;
}
rev(lson, l, mid, x, y);
rev(rson, mid+1, r, x, y);
if (c[lson]==c[rson] && c[lson]!=-1)
c[k]=c[lson];
}
}
void update(int k, int l, int r){
if (l==r)
fc[l]=c[k];
else{
int mid=(l+r)>>1, lson=(k<<1), rson=(k<<1)+1;
if (c[k]!=-1)
c[lson]=c[rson]=c[k];
update(lson, l, mid);
update(rson, mid+1, r);
}
}
void putans(){
int i=0;
bool empty=true;
update(1, 0, maxn-1);
fc[0]=fc[maxn-1]=0;
while (1){
while (fc[i]==0 && i<maxn-1) i++;
if (i>=maxn-1) break;
if (!empty) printf(" ");
printf("%c%d,", (i%3==1)? '[' : '(', i/3);
while (fc[i]==1) i++; i--;
printf("%d%c", i/3, (i%3==1)? ']' : ')');
empty=false;
i++;
}
if (empty) printf("empty set/n");
else printf("/n");
}
int main(){
char mode, lc, rc;
int a,b;
while (1){
mode=' ';
while (mode<'A' || mode>'Z')
if ((mode=getchar())==EOF) return 0;
memset(c, 0, sizeof(short)*maxn*3);
while (mode!='E'){
lc=' ';
while (getchar()!=' ') getchar();
scanf("%c%d,%d%c",&lc,&a, &b, &rc);
a=(lc=='(') ? a*3+2 : a*3+1;
b=(rc==')') ? b*3 : b*3+1;
switch(mode){
case 'U':   print(1, 0, maxn-1, a, b, 1);
break;
case 'D':   print(1, 0, maxn-1, a, b, 0);
break;
case 'C':   rev(1, 0, maxn-1, a, b);
case 'I':   print(1, 0, maxn-1, 0, a-1, 0);
print(1, 0, maxn-1, b+1, maxn-1, 0);
break;
case 'S':   rev(1, 0, maxn-1, a, b);
break;
}
mode=' ';
while (mode<'A' || mode>'Z')
mode=getchar();
}
putans();
getchar(); getchar();       //"ND" in End
}
return 0;
}


G 网络流

还没有写,大概想了一下构图,但是可能不是最好的。

字符串处理也比较不熟,用STL应该会比较好。

对网络流的算法不是很熟悉,以后找一段时间来突击一下。

H 快速幂

如果能往快速幂方向去想了应该就能比较快得到这个思路,只可惜比赛的时候很二地去推通项,以为能得到一个带有规律的迭代项……

这一道题在long long和int上纠结了比较长时间,太弱了。起初打算偷懒,变量都写成long long的,但是中途发现各种问题,就改掉了。最后得出的结论是,其实int都够用,只是偶尔中间有大数乘积的时候需要用long long暂时放一放再%prime。

long long的输入还是用cin比较靠谱(输出用cout,这题没用到)。还得到一个结果是__int64或者long long最好不要直接和0比较,这一次挂掉了,虽然不知道理论原因,不过知道了可以改写成“if (abs(n-0)<0.5) break;”这样的判断。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
#define LL long long
#define maxn 17
typedef int matrix[maxn][maxn];

void ident(int size, matrix x){    //构造单位阵
for (int i=0; i<size; ++i)
for (int j=0; j<size; ++j)
x[i][j]=(i==j?1:0);
}
void copy(int size, matrix x, matrix y){        //矩阵复制
for (int i=0; i<size; ++i)
for (int j=0; j<size; ++j)
y[i][j]=x[i][j];
}
void mutiply(int size, matrix _x, matrix _y, matrix z, LL M){
matrix x, y;
copy(size, _x, x);
copy(size, _y, y);
for (int i=0; i<size; ++i)
for (int j=0; j<size; ++j){
z[i][j]=0;
for (int k=0; k<size; ++k)
z[i][j]=((LL)z[i][j]+(LL)x[i][k]*y[k][j])%M;
}
}

void power(int size, matrix _x, LL n, matrix y, LL M){
matrix x, r;
copy(size, _x, x);
ident(size, r);
while (n){
if (n&1) mutiply(size, r, x, r, M);
n>>=1;
if (abs(n-0)<0.5) break;
mutiply(size, x, x, x, M);
}
copy(size, r, y);
}
bool init(int &k, LL &n, LL &m, int &p, matrix a, int b[], int c[]){
if (scanf("%d", &k)!=1) return false;
for (int i=0; i<k; i++)
scanf("%d", &b[i]);
for (int i=0; i<k; i++)
scanf("%d", &c[i]);
cin>>m>>n>>p;
b[k]=0;     //b[k]=sumb
for (int i=0; i<k; i++)
b[k]=(b[k]+b[i])%p;
memset(a, 0, sizeof(int)*maxn*maxn);
for (int i=0; i<k-1; i++)
a[i+1][i]=1;
for (int i=0; i<k; i++)
a[i][k-1]=a[i][k]=(c[k-i-1])%p;
a[k][k]=1;
return true;
}
void getsum(matrix s, int k, int p, int b[], int &sum){
sum=0;
for (int i=0; i<k+1; i++)
sum=((LL)sum+((LL)b[i]*s[i][k]))%p;
}
int main(){
matrix a, s;
LL n,m;
int b[maxn],c[maxn];
int k,sn,sm,p;
while (init(k, n, m, p, a, b, c)){
if (n>k){
power(k+1, a, n-k, s, p);
getsum(s, k, p, b, sn);
}
else{
sn=0;
for (int i=0; i<n; i++) sn=(sn+b[i])%p;
}
if (m-1>k){
power(k+1, a, m-k-1, s, p);
getsum(s, k, p, b, sm);
}
else{
sm=0;
for (int i=0; i<m-1; i++) sm=(sm+b[i])%p;
}
printf("%d/n",(sn-sm+p)%p);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: